Merge "Use MaskableIconDrawable only in dogfood builds" into ub-launcher3-master
diff --git a/build.gradle b/build.gradle
index 00667f1..3a812a9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -72,7 +72,7 @@
     androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
     androidTestCompile 'com.android.support.test:runner:0.5'
     androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
-    androidTestCompile 'com.android.support:support-annotations:23.2.0'
+    androidTestCompile "com.android.support:support-annotations:${SUPPORT_LIBS_VERSION}"
 }
 
 protobuf {
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a8a25af..1a09fa0 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -81,8 +81,6 @@
 
     <dimen name="all_apps_divider_margin_vertical">8dp</dimen>
 
-    <dimen name="all_apps_bezel_swipe_height">24dp</dimen>
-
 <!-- Widget tray -->
     <dimen name="widget_preview_label_vertical_padding">8dp</dimen>
     <dimen name="widget_preview_label_horizontal_padding">8dp</dimen>
diff --git a/res/xml/app_target_browser.xml b/res/xml/app_target_browser.xml
deleted file mode 100644
index d7c3ed5..0000000
--- a/res/xml/app_target_browser.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
-    <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
-    <favorite launcher:uri="http://www.example.com/" />
-
-</resolve>
\ No newline at end of file
diff --git a/res/xml/app_target_camera.xml b/res/xml/app_target_camera.xml
deleted file mode 100644
index f65a2b1..0000000
--- a/res/xml/app_target_camera.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
-    <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
-    <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
-
-</resolve>
\ No newline at end of file
diff --git a/res/xml/app_target_email.xml b/res/xml/app_target_email.xml
deleted file mode 100644
index 44f0a40..0000000
--- a/res/xml/app_target_email.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
-    <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
-    <favorite launcher:uri="mailto:" />
-
-</resolve>
\ No newline at end of file
diff --git a/res/xml/app_target_gallery.xml b/res/xml/app_target_gallery.xml
deleted file mode 100644
index c9d3492..0000000
--- a/res/xml/app_target_gallery.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
-    <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
-    <favorite launcher:uri="#Intent;type=images/*;end" />
-
-</resolve>
\ No newline at end of file
diff --git a/res/xml/app_target_messenger.xml b/res/xml/app_target_messenger.xml
deleted file mode 100644
index 278eb5c..0000000
--- a/res/xml/app_target_messenger.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
-    <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
-    <favorite launcher:uri="sms:" />
-    <favorite launcher:uri="smsto:" />
-    <favorite launcher:uri="mms:" />
-    <favorite launcher:uri="mmsto:" />
-
-</resolve>
\ No newline at end of file
diff --git a/res/xml/app_target_phone.xml b/res/xml/app_target_phone.xml
deleted file mode 100644
index 5d6ca31..0000000
--- a/res/xml/app_target_phone.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
-
-    <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
-    <favorite launcher:uri="tel:123" />
-    <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
-
-</resolve>
\ No newline at end of file
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 34a44fc..9cce9b1 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -18,9 +18,9 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.LauncherActivityInfo;
 import android.os.UserHandle;
 
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -65,13 +65,15 @@
      *
      * If the app is already in the list, doesn't add it.
      */
-    public void add(AppInfo info) {
+    public void add(AppInfo info, LauncherActivityInfo activityInfo) {
         if (!mAppFilter.shouldShowApp(info.componentName)) {
             return;
         }
         if (findActivity(data, info.componentName, info.user)) {
             return;
         }
+        mIconCache.getTitleAndIcon(info, activityInfo, true /* useLowResIcon */);
+
         data.add(info);
         added.add(info);
     }
@@ -97,11 +99,11 @@
      */
     public void addPackage(Context context, String packageName, UserHandle user) {
         final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
-        final List<LauncherActivityInfoCompat> matches = launcherApps.getActivityList(packageName,
+        final List<LauncherActivityInfo> matches = launcherApps.getActivityList(packageName,
                 user);
 
-        for (LauncherActivityInfoCompat info : matches) {
-            add(new AppInfo(context, info, user, mIconCache));
+        for (LauncherActivityInfo info : matches) {
+            add(new AppInfo(context, info, user), info);
         }
     }
 
@@ -148,7 +150,7 @@
      */
     public void updatePackage(Context context, String packageName, UserHandle user) {
         final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
-        final List<LauncherActivityInfoCompat> matches = launcherApps.getActivityList(packageName,
+        final List<LauncherActivityInfo> matches = launcherApps.getActivityList(packageName,
                 user);
         if (matches.size() > 0) {
             // Find disabled/removed activities and remove them from data and add them
@@ -166,12 +168,12 @@
 
             // Find enabled activities and add them to the adapter
             // Also updates existing activities with new labels/icons
-            for (final LauncherActivityInfoCompat info : matches) {
+            for (final LauncherActivityInfo info : matches) {
                 AppInfo applicationInfo = findApplicationInfoLocked(
                         info.getComponentName().getPackageName(), user,
                         info.getComponentName().getClassName());
                 if (applicationInfo == null) {
-                    add(new AppInfo(context, info, user, mIconCache));
+                    add(new AppInfo(context, info, user), info);
                 } else {
                     mIconCache.getTitleAndIcon(applicationInfo, info, true /* useLowResIcon */);
                     modified.add(applicationInfo);
@@ -195,9 +197,9 @@
     /**
      * Returns whether <em>apps</em> contains <em>component</em>.
      */
-    private static boolean findActivity(List<LauncherActivityInfoCompat> apps,
+    private static boolean findActivity(List<LauncherActivityInfo> apps,
             ComponentName component) {
-        for (LauncherActivityInfoCompat info : apps) {
+        for (LauncherActivityInfo info : apps) {
             if (info.getComponentName().equals(component)) {
                 return true;
             }
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 9c9dcc5..6bec997 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -19,10 +19,10 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
 import android.os.UserHandle;
 import android.util.Log;
 
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -58,19 +58,12 @@
     /**
      * Must not hold the Context.
      */
-    public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandle user,
-            IconCache iconCache) {
-        this(context, info, user, iconCache,
-                UserManagerCompat.getInstance(context).isQuietModeEnabled(user));
+    public AppInfo(Context context, LauncherActivityInfo info, UserHandle user) {
+        this(context, info, user, UserManagerCompat.getInstance(context).isQuietModeEnabled(user));
     }
 
-    public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandle user,
-            IconCache iconCache, boolean quietModeEnabled) {
-        this(context, info, user, iconCache, quietModeEnabled, true /* useLowResIcon */);
-    }
-
-    public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandle user,
-            IconCache iconCache, boolean quietModeEnabled, boolean useLowResIcon) {
+    public AppInfo(Context context, LauncherActivityInfo info, UserHandle user,
+            boolean quietModeEnabled) {
         this.componentName = info.getComponentName();
         this.container = ItemInfo.NO_ID;
         this.user = user;
@@ -82,7 +75,6 @@
         }
 
         intent = makeLaunchIntent(context, info, user);
-        iconCache.getTitleAndIcon(this, info, useLowResIcon);
     }
 
     public AppInfo(AppInfo info) {
@@ -118,7 +110,7 @@
         return new ComponentKey(componentName, user);
     }
 
-    public static Intent makeLaunchIntent(Context context, LauncherActivityInfoCompat info,
+    public static Intent makeLaunchIntent(Context context, LauncherActivityInfo info,
             UserHandle user) {
         long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
         return new Intent(Intent.ACTION_MAIN)
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index f879216..1a405f9 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -212,6 +212,23 @@
         lp.height = mTempRange1.size();
 
         resizeWidgetIfNeeded(false);
+
+        // When the widget resizes in multi-window mode, the translation value changes to maintain
+        // a center fit. These overrides ensure the resize frame always aligns with the widget view.
+        getSnappedRectRelativeToDragLayer(sTmpRect);
+        if (mLeftBorderActive) {
+            lp.width = sTmpRect.width() + sTmpRect.left - lp.x;
+        }
+        if (mTopBorderActive) {
+            lp.height = sTmpRect.height() + sTmpRect.top - lp.y;
+        }
+        if (mRightBorderActive) {
+            lp.x = sTmpRect.left;
+        }
+        if (mBottomBorderActive) {
+            lp.y = sTmpRect.top;
+        }
+
         requestLayout();
     }
 
@@ -303,7 +320,7 @@
 
     public static Rect getWidgetSizeRanges(Context context, int spanX, int spanY, Rect rect) {
         if (sCellSize == null) {
-            InvariantDeviceProfile inv = LauncherAppState.getInstance().getInvariantDeviceProfile();
+            InvariantDeviceProfile inv = LauncherAppState.getIDP(context);
 
             // Initiate cell sizes.
             sCellSize = new Point[2];
@@ -340,8 +357,8 @@
         int xThreshold = mCellLayout.getCellWidth();
         int yThreshold = mCellLayout.getCellHeight();
 
-        mDeltaXAddOn = mRunningHInc * xThreshold; 
-        mDeltaYAddOn = mRunningVInc * yThreshold; 
+        mDeltaXAddOn = mRunningHInc * xThreshold;
+        mDeltaYAddOn = mRunningVInc * yThreshold;
         mDeltaX = 0;
         mDeltaY = 0;
 
@@ -353,18 +370,35 @@
         });
     }
 
-    public void snapToWidget(boolean animate) {
+    /**
+     * Returns the rect of this view when the frame is snapped around the widget, with the bounds
+     * relative to the {@link DragLayer}.
+     */
+    private void getSnappedRectRelativeToDragLayer(Rect out) {
         float scale = mWidgetView.getScaleToFit();
 
-        mDragLayer.getViewRectRelativeToSelf(mWidgetView, sTmpRect);
+        mDragLayer.getViewRectRelativeToSelf(mWidgetView, out);
 
-        int newWidth = 2 * mBackgroundPadding
-                + (int) (scale * (sTmpRect.width() - mWidgetPadding.left - mWidgetPadding.right));
-        int newHeight = 2 * mBackgroundPadding
-                + (int) (scale * (sTmpRect.height() - mWidgetPadding.top - mWidgetPadding.bottom));
+        int width = 2 * mBackgroundPadding
+                + (int) (scale * (out.width() - mWidgetPadding.left - mWidgetPadding.right));
+        int height = 2 * mBackgroundPadding
+                + (int) (scale * (out.height() - mWidgetPadding.top - mWidgetPadding.bottom));
 
-        int newX = (int) (sTmpRect.left - mBackgroundPadding + scale * mWidgetPadding.left);
-        int newY = (int) (sTmpRect.top - mBackgroundPadding + scale * mWidgetPadding.top);
+        int x = (int) (out.left - mBackgroundPadding + scale * mWidgetPadding.left);
+        int y = (int) (out.top - mBackgroundPadding + scale * mWidgetPadding.top);
+
+        out.left = x;
+        out.top = y;
+        out.right = out.left + width;
+        out.bottom = out.top + height;
+    }
+
+    public void snapToWidget(boolean animate) {
+        getSnappedRectRelativeToDragLayer(sTmpRect);
+        int newWidth = sTmpRect.width();
+        int newHeight = sTmpRect.height();
+        int newX = sTmpRect.left;
+        int newY = sTmpRect.top;
 
         // We need to make sure the frame's touchable regions lie fully within the bounds of the
         // DragLayer. We allow the actual handles to be clipped, but we shift the touch regions
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index c5b3104..c6f1c48 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -5,13 +5,13 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
 import android.util.Log;
 
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.util.ContentWriter;
 
 public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
 
@@ -50,14 +50,13 @@
                 state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
             }
 
-            ContentValues values = new ContentValues();
-            values.put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i]);
-            values.put(LauncherSettings.Favorites.RESTORED, state);
-
             String[] widgetIdParams = new String[] { Integer.toString(oldWidgetIds[i]) };
+            int result = new ContentWriter(context, new ContentWriter.CommitParams(
+                    "appWidgetId=? and (restored & 1) = 1", widgetIdParams))
+                    .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
+                    .put(LauncherSettings.Favorites.RESTORED, state)
+                    .commit();
 
-            int result = cr.update(Favorites.CONTENT_URI, values,
-                    "appWidgetId=? and (restored & 1) = 1", widgetIdParams);
             if (result == 0) {
                 Cursor cursor = cr.query(Favorites.CONTENT_URI,
                         new String[] {Favorites.APPWIDGET_ID},
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 2a4212a..2e017df 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -79,7 +79,7 @@
 
     static AutoInstallsLayout get(Context context, String pkg, Resources targetRes,
             AppWidgetHost appWidgetHost, LayoutParserCallback callback) {
-        InvariantDeviceProfile grid = LauncherAppState.getInstance().getInvariantDeviceProfile();
+        InvariantDeviceProfile grid = LauncherAppState.getIDP(context);
 
         // Try with grid size and hotseat count
         String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT,
@@ -182,7 +182,7 @@
         mSourceRes = res;
         mLayoutId = layoutId;
 
-        mIdp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+        mIdp = LauncherAppState.getIDP(context);
         mRowCount = mIdp.numRows;
         mColumnCount = mIdp.numColumns;
     }
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
index 40c5ed6..4fecc3d 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
@@ -28,6 +28,8 @@
 import android.view.ViewConfiguration;
 import android.widget.TextView;
 
+import com.android.launcher3.config.FeatureFlags;
+
 /**
  * The track and scrollbar that shows when you scroll the list.
  */
@@ -198,6 +200,11 @@
             case MotionEvent.ACTION_DOWN:
                 if (isNearThumb(downX, downY)) {
                     mTouchOffsetY = downY - mThumbOffsetY;
+                } else if (FeatureFlags.LAUNCHER3_DIRECT_SCROLL
+                        && mRv.supportsFastScrolling()
+                        && isNearScrollBar(downX)) {
+                    calcTouchOffsetAndPrepToFastScroll(downY, lastY);
+                    updateFastScrollSectionNameAndThumbOffset(lastY, y);
                 }
                 break;
             case MotionEvent.ACTION_MOVE:
@@ -207,28 +214,10 @@
                 if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() &&
                         isNearThumb(downX, lastY) &&
                         Math.abs(y - downY) > config.getScaledTouchSlop()) {
-                    mRv.getParent().requestDisallowInterceptTouchEvent(true);
-                    mIsDragging = true;
-                    if (mCanThumbDetach) {
-                        mIsThumbDetached = true;
-                    }
-                    mTouchOffsetY += (lastY - downY);
-                    animatePopupVisibility(true);
-                    showActiveScrollbar(true);
+                    calcTouchOffsetAndPrepToFastScroll(downY, lastY);
                 }
                 if (mIsDragging) {
-                    // Update the fastscroller section name at this touch position
-                    int bottom = mRv.getScrollbarTrackHeight() - mThumbHeight;
-                    float boundedY = (float) Math.max(0, Math.min(bottom, y - mTouchOffsetY));
-                    String sectionName = mRv.scrollToPositionAtProgress(boundedY / bottom);
-                    if (!sectionName.equals(mPopupSectionName)) {
-                        mPopupSectionName = sectionName;
-                        mPopupView.setText(sectionName);
-                    }
-                    animatePopupVisibility(!sectionName.isEmpty());
-                    updatePopupY(lastY);
-                    mLastTouchY = boundedY;
-                    setThumbOffsetY((int) mLastTouchY);
+                    updateFastScrollSectionNameAndThumbOffset(lastY, y);
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -245,6 +234,32 @@
         }
     }
 
+    private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
+        mRv.getParent().requestDisallowInterceptTouchEvent(true);
+        mIsDragging = true;
+        if (mCanThumbDetach) {
+            mIsThumbDetached = true;
+        }
+        mTouchOffsetY += (lastY - downY);
+        animatePopupVisibility(true);
+        showActiveScrollbar(true);
+    }
+
+    private void updateFastScrollSectionNameAndThumbOffset(int lastY, int y) {
+        // Update the fastscroller section name at this touch position
+        int bottom = mRv.getScrollbarTrackHeight() - mThumbHeight;
+        float boundedY = (float) Math.max(0, Math.min(bottom, y - mTouchOffsetY));
+        String sectionName = mRv.scrollToPositionAtProgress(boundedY / bottom);
+        if (!sectionName.equals(mPopupSectionName)) {
+            mPopupSectionName = sectionName;
+            mPopupView.setText(sectionName);
+        }
+        animatePopupVisibility(!sectionName.isEmpty());
+        updatePopupY(lastY);
+        mLastTouchY = boundedY;
+        setThumbOffsetY((int) mLastTouchY);
+    }
+
     public void draw(Canvas canvas) {
         if (mThumbOffsetY < 0) {
             return;
@@ -277,7 +292,7 @@
     }
 
     /**
-     * Returns whether the specified points are near the scroll bar bounds.
+     * Returns whether the specified point is inside the thumb bounds.
      */
     public boolean isNearThumb(int x, int y) {
         int left = getDrawLeft();
@@ -286,6 +301,14 @@
         return mTmpRect.contains(x, y);
     }
 
+    /**
+     * Returns whether the specified x position is near the scroll bar.
+     */
+    public boolean isNearScrollBar(int x) {
+        int left = getDrawLeft();
+        return x >= left && x <= left + mMaxWidth;
+    }
+
     private void animatePopupVisibility(boolean visible) {
         if (mPopupVisible != visible) {
             mPopupVisible = visible;
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 5e9e7e2..32c3ac8 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -38,6 +38,7 @@
 import android.widget.TextView;
 
 import com.android.launcher3.IconCache.IconLoadRequest;
+import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.graphics.HolographicOutlineHelper;
@@ -51,7 +52,7 @@
  * too aggressive.
  */
 public class BubbleTextView extends TextView
-        implements BaseRecyclerViewFastScrollBar.FastScrollFocusableView {
+        implements BaseRecyclerViewFastScrollBar.FastScrollFocusableView, ItemInfoUpdateReceiver {
 
     private static SparseArray<Theme> sPreloaderThemes = new SparseArray<Theme>(2);
 
@@ -540,7 +541,8 @@
     /**
      * Applies the item info if it is same as what the view is pointing to currently.
      */
-    public void reapplyItemInfo(final ItemInfo info) {
+    @Override
+    public void reapplyItemInfo(ItemInfoWithIcon info) {
         if (getTag() == info) {
             FastBitmapDrawable.State prevState = FastBitmapDrawable.State.NORMAL;
             if (mIcon instanceof FastBitmapDrawable) {
@@ -582,22 +584,10 @@
             mIconLoadRequest.cancel();
             mIconLoadRequest = null;
         }
-        if (getTag() instanceof AppInfo) {
-            AppInfo info = (AppInfo) getTag();
+        if (getTag() instanceof ItemInfoWithIcon) {
+            ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
             if (info.usingLowResIcon) {
-                mIconLoadRequest = LauncherAppState.getInstance().getIconCache()
-                        .updateIconInBackground(BubbleTextView.this, info);
-            }
-        } else if (getTag() instanceof ShortcutInfo) {
-            ShortcutInfo info = (ShortcutInfo) getTag();
-            if (info.usingLowResIcon) {
-                mIconLoadRequest = LauncherAppState.getInstance().getIconCache()
-                        .updateIconInBackground(BubbleTextView.this, info);
-            }
-        } else if (getTag() instanceof PackageItemInfo) {
-            PackageItemInfo info = (PackageItemInfo) getTag();
-            if (info.usingLowResIcon) {
-                mIconLoadRequest = LauncherAppState.getInstance().getIconCache()
+                mIconLoadRequest = LauncherAppState.getInstance(getContext()).getIconCache()
                         .updateIconInBackground(BubbleTextView.this, info);
             }
         }
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 70c8739..da12e67 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2096,6 +2096,7 @@
                 a.cancel();
             }
 
+            setInitialAnimationValues(true);
             a = new LauncherViewPropertyAnimator(child)
                 .scaleX(initScale)
                 .scaleY(initScale)
diff --git a/src/com/android/launcher3/CommonAppTypeParser.java b/src/com/android/launcher3/CommonAppTypeParser.java
deleted file mode 100644
index c2bd883..0000000
--- a/src/com/android/launcher3/CommonAppTypeParser.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.XmlResourceParser;
-import android.database.sqlite.SQLiteDatabase;
-import android.util.Log;
-
-import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.util.Thunk;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-/**
- * A class that parses content values corresponding to some common app types.
- */
-public class CommonAppTypeParser implements LayoutParserCallback {
-    private static final String TAG = "CommonAppTypeParser";
-
-    // Including TARGET_NONE
-    public static final int SUPPORTED_TYPE_COUNT = 7;
-
-    private static final int RESTORE_FLAG_BIT_SHIFT = 4;
-
-    public static final int TARGET_PHONE = 1;
-    public static final int TARGET_MESSENGER = 2;
-    public static final int TARGET_EMAIL = 3;
-    public static final int TARGET_BROWSER = 4;
-    public static final int TARGET_GALLERY = 5;
-    public static final int TARGET_CAMERA = 6;
-
-    private final long mItemId;
-    @Thunk final int mResId;
-    @Thunk final Context mContext;
-
-    ContentValues parsedValues;
-    Intent parsedIntent;
-    String parsedTitle;
-
-    public CommonAppTypeParser(long itemId, int itemType, Context context) {
-        mItemId = itemId;
-        mContext = context;
-        mResId = getResourceForItemType(itemType);
-    }
-
-    @Override
-    public long generateNewItemId() {
-        return mItemId;
-    }
-
-    @Override
-    public long insertAndCheck(SQLiteDatabase db, ContentValues values) {
-        parsedValues = values;
-
-        // Remove unwanted values
-        values.put(Favorites.ICON_PACKAGE, (String) null);
-        values.put(Favorites.ICON_RESOURCE, (String) null);
-        values.put(Favorites.ICON, (byte[]) null);
-        return 1;
-    }
-
-    /**
-     * Tries to find a suitable app to the provided app type.
-     */
-    public boolean findDefaultApp() {
-        if (mResId == 0) {
-            return false;
-        }
-
-        parsedIntent = null;
-        parsedValues = null;
-        new MyLayoutParser().parseValues();
-        return (parsedValues != null) && (parsedIntent != null);
-    }
-
-    private class MyLayoutParser extends DefaultLayoutParser {
-
-        public MyLayoutParser() {
-            super(CommonAppTypeParser.this.mContext, null, CommonAppTypeParser.this,
-                    CommonAppTypeParser.this.mContext.getResources(), mResId, TAG_RESOLVE);
-        }
-
-        @Override
-        protected long addShortcut(String title, Intent intent, int type) {
-            if (type == Favorites.ITEM_TYPE_APPLICATION) {
-                parsedIntent = intent;
-                parsedTitle = title;
-            }
-            return super.addShortcut(title, intent, type);
-        }
-
-        public void parseValues() {
-            XmlResourceParser parser = mSourceRes.getXml(mLayoutId);
-            try {
-                beginDocument(parser, mRootTag);
-                new ResolveParser().parseAndAdd(parser);
-            } catch (IOException | XmlPullParserException e) {
-                Log.e(TAG, "Unable to parse default app info", e);
-            }
-            parser.close();
-        }
-    }
-
-    public static int getResourceForItemType(int type) {
-        switch (type) {
-            case TARGET_PHONE:
-                return R.xml.app_target_phone;
-
-            case TARGET_MESSENGER:
-                return R.xml.app_target_messenger;
-
-            case TARGET_EMAIL:
-                return R.xml.app_target_email;
-
-            case TARGET_BROWSER:
-                return R.xml.app_target_browser;
-
-            case TARGET_GALLERY:
-                return R.xml.app_target_gallery;
-
-            case TARGET_CAMERA:
-                return R.xml.app_target_camera;
-
-            default:
-                return 0;
-        }
-    }
-
-    public static int encodeItemTypeToFlag(int itemType) {
-        return itemType << RESTORE_FLAG_BIT_SHIFT;
-    }
-
-    public static int decodeItemTypeFromFlag(int flag) {
-        return (flag & ShortcutInfo.FLAG_RESTORED_APP_TYPE) >> RESTORE_FLAG_BIT_SHIFT;
-    }
-
-}
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index ef28d1e..05911ab 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -54,11 +54,6 @@
         super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES);
     }
 
-    public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
-            LayoutParserCallback callback, Resources sourceRes, int layoutId, String rootTag) {
-        super(context, appWidgetHost, callback, sourceRes, layoutId, rootTag);
-    }
-
     @Override
     protected HashMap<String, TagParser> getFolderElementsMap() {
         return getFolderElementsMap(mSourceRes);
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index b22cb7c..6535d65 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -45,13 +46,13 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.Provider;
 import com.android.launcher3.util.SQLiteCacheHelper;
 import com.android.launcher3.util.Thunk;
@@ -188,7 +189,7 @@
         return getFullResDefaultActivityIcon();
     }
 
-    private Bitmap makeDefaultIcon(UserHandle user) {
+    protected Bitmap makeDefaultIcon(UserHandle user) {
         Drawable unbadged = getFullResDefaultActivityIcon();
         return LauncherIcons.createBadgedIconBitmap(unbadged, user, mContext);
     }
@@ -225,7 +226,7 @@
             PackageInfo info = mPackageManager.getPackageInfo(packageName,
                     PackageManager.GET_UNINSTALLED_PACKAGES);
             long userSerial = mUserManager.getSerialNumberForUser(user);
-            for (LauncherActivityInfoCompat app : mLauncherApps.getActivityList(packageName, user)) {
+            for (LauncherActivityInfo app : mLauncherApps.getActivityList(packageName, user)) {
                 addIconToDBAndMemCache(app, info, userSerial, false /*replace existing*/);
             }
         } catch (NameNotFoundException e) {
@@ -252,7 +253,7 @@
         mIconProvider.updateSystemStateString();
         for (UserHandle user : mUserManager.getUserProfiles()) {
             // Query for the set of apps
-            final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
+            final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
             // Fail if we don't have any apps
             // TODO: Fix this. Only fail for the current user.
             if (apps == null || apps.isEmpty()) {
@@ -271,7 +272,7 @@
      * the DB and are updated.
      * @return The set of packages for which icons have updated.
      */
-    private void updateDBIcons(UserHandle user, List<LauncherActivityInfoCompat> apps,
+    private void updateDBIcons(UserHandle user, List<LauncherActivityInfo> apps,
             Set<String> ignorePackages) {
         long userSerial = mUserManager.getSerialNumberForUser(user);
         PackageManager pm = mContext.getPackageManager();
@@ -280,13 +281,13 @@
             pkgInfoMap.put(info.packageName, info);
         }
 
-        HashMap<ComponentName, LauncherActivityInfoCompat> componentMap = new HashMap<>();
-        for (LauncherActivityInfoCompat app : apps) {
+        HashMap<ComponentName, LauncherActivityInfo> componentMap = new HashMap<>();
+        for (LauncherActivityInfo app : apps) {
             componentMap.put(app.getComponentName(), app);
         }
 
         HashSet<Integer> itemsToRemove = new HashSet<Integer>();
-        Stack<LauncherActivityInfoCompat> appsToUpdate = new Stack<>();
+        Stack<LauncherActivityInfo> appsToUpdate = new Stack<>();
 
         Cursor c = null;
         try {
@@ -321,7 +322,7 @@
 
                 long updateTime = c.getLong(indexLastUpdate);
                 int version = c.getInt(indexVersion);
-                LauncherActivityInfoCompat app = componentMap.remove(component);
+                LauncherActivityInfo app = componentMap.remove(component);
                 if (version == info.versionCode && updateTime == info.lastUpdateTime &&
                         TextUtils.equals(c.getString(systemStateIndex),
                                 mIconProvider.getIconSystemState(info.packageName))) {
@@ -349,7 +350,7 @@
 
         // Insert remaining apps.
         if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) {
-            Stack<LauncherActivityInfoCompat> appsToAdd = new Stack<>();
+            Stack<LauncherActivityInfo> appsToAdd = new Stack<>();
             appsToAdd.addAll(componentMap.values());
             new SerializedIconUpdateTask(userSerial, pkgInfoMap,
                     appsToAdd, appsToUpdate).scheduleNext();
@@ -362,7 +363,7 @@
      *                        the memory. This is useful then the previous bitmap was created using
      *                        old data.
      */
-    @Thunk synchronized void addIconToDBAndMemCache(LauncherActivityInfoCompat app,
+    @Thunk synchronized void addIconToDBAndMemCache(LauncherActivityInfo app,
             PackageInfo info, long userSerial, boolean replaceExisting) {
         final ComponentKey key = new ComponentKey(app.getComponentName(), app.getUser());
         CacheEntry entry = null;
@@ -406,13 +407,14 @@
      * Fetches high-res icon for the provided ItemInfo and updates the caller when done.
      * @return a request ID that can be used to cancel the request.
      */
-    public IconLoadRequest updateIconInBackground(final BubbleTextView caller, final ItemInfo info) {
+    public IconLoadRequest updateIconInBackground(final ItemInfoUpdateReceiver caller,
+            final ItemInfoWithIcon info) {
         Runnable request = new Runnable() {
 
             @Override
             public void run() {
                 if (info instanceof AppInfo || info instanceof ShortcutInfo) {
-                    getTitleAndIcon((ItemInfoWithIcon) info, false);
+                    getTitleAndIcon(info, false);
                 } else if (info instanceof PackageItemInfo) {
                     getTitleAndIconForApp((PackageItemInfo) info, false);
                 }
@@ -430,25 +432,11 @@
     }
 
     /**
-     * Returns a high res icon for the given intent and user
-     */
-    public synchronized Bitmap getIcon(Intent intent, UserHandle user) {
-        ComponentName component = intent.getComponent();
-        // null info means not installed, but if we have a component from the intent then
-        // we should still look in the cache for restored app icons.
-        if (component == null) {
-            return getDefaultIcon(user);
-        }
-        return cacheLocked(component, new ActivityInfoProvider(intent, user),
-                user, true, false /* useLowRes */).icon;
-    }
-
-    /**
      * Updates {@param application} only if a valid entry is found.
      */
     public synchronized void updateTitleAndIcon(AppInfo application) {
         CacheEntry entry = cacheLocked(application.componentName,
-                Provider.<LauncherActivityInfoCompat>of(null),
+                Provider.<LauncherActivityInfo>of(null),
                 application.user, false, application.usingLowResIcon);
         if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) {
             applyCacheEntry(entry, application);
@@ -459,7 +447,7 @@
      * Fill in {@param info} with the icon and label for {@param activityInfo}
      */
     public synchronized void getTitleAndIcon(ItemInfoWithIcon info,
-            LauncherActivityInfoCompat activityInfo, boolean useLowResIcon) {
+            LauncherActivityInfo activityInfo, boolean useLowResIcon) {
         // If we already have activity info, no need to use package icon
         getTitleAndIcon(info, Provider.of(activityInfo), false, useLowResIcon);
     }
@@ -487,7 +475,7 @@
      */
     private synchronized void getTitleAndIcon(
             @NonNull ItemInfoWithIcon infoInOut,
-            @NonNull Provider<LauncherActivityInfoCompat> activityInfoProvider,
+            @NonNull Provider<LauncherActivityInfo> activityInfoProvider,
             boolean usePkgIcon, boolean useLowResIcon) {
         CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), activityInfoProvider,
                 infoInOut.user, usePkgIcon, useLowResIcon);
@@ -528,8 +516,9 @@
      */
     protected CacheEntry cacheLocked(
             @NonNull ComponentName componentName,
-            @NonNull Provider<LauncherActivityInfoCompat> infoProfider,
+            @NonNull Provider<LauncherActivityInfo> infoProvider,
             UserHandle user, boolean usePackageIcon, boolean useLowResIcon) {
+        Preconditions.assertWorkerThread();
         ComponentKey cacheKey = new ComponentKey(componentName, user);
         CacheEntry entry = mCache.get(cacheKey);
         if (entry == null || (entry.isLowResIcon && !useLowResIcon)) {
@@ -537,11 +526,11 @@
             mCache.put(cacheKey, entry);
 
             // Check the DB first.
-            LauncherActivityInfoCompat info = null;
+            LauncherActivityInfo info = null;
             boolean providerFetchedOnce = false;
 
             if (!getEntryFromDB(cacheKey, entry, useLowResIcon) || DEBUG_IGNORE_CACHE) {
-                info = infoProfider.get();
+                info = infoProvider.get();
                 providerFetchedOnce = true;
 
                 if (info != null) {
@@ -570,7 +559,7 @@
 
             if (TextUtils.isEmpty(entry.title)) {
                 if (info == null && !providerFetchedOnce) {
-                    info = infoProfider.get();
+                    info = infoProvider.get();
                     providerFetchedOnce = true;
                 }
                 if (info != null) {
@@ -617,6 +606,7 @@
      */
     private CacheEntry getEntryForPackageLocked(String packageName, UserHandle user,
             boolean useLowResIcon) {
+        Preconditions.assertWorkerThread();
         ComponentKey cacheKey = getPackageKey(packageName, user);
         CacheEntry entry = mCache.get(cacheKey);
 
@@ -714,19 +704,19 @@
 
     /**
      * A runnable that updates invalid icons and adds missing icons in the DB for the provided
-     * LauncherActivityInfoCompat list. Items are updated/added one at a time, so that the
+     * LauncherActivityInfo list. Items are updated/added one at a time, so that the
      * worker thread doesn't get blocked.
      */
     @Thunk class SerializedIconUpdateTask implements Runnable {
         private final long mUserSerial;
         private final HashMap<String, PackageInfo> mPkgInfoMap;
-        private final Stack<LauncherActivityInfoCompat> mAppsToAdd;
-        private final Stack<LauncherActivityInfoCompat> mAppsToUpdate;
+        private final Stack<LauncherActivityInfo> mAppsToAdd;
+        private final Stack<LauncherActivityInfo> mAppsToUpdate;
         private final HashSet<String> mUpdatedPackages = new HashSet<String>();
 
         @Thunk SerializedIconUpdateTask(long userSerial, HashMap<String, PackageInfo> pkgInfoMap,
-                Stack<LauncherActivityInfoCompat> appsToAdd,
-                Stack<LauncherActivityInfoCompat> appsToUpdate) {
+                Stack<LauncherActivityInfo> appsToAdd,
+                Stack<LauncherActivityInfo> appsToUpdate) {
             mUserSerial = userSerial;
             mPkgInfoMap = pkgInfoMap;
             mAppsToAdd = appsToAdd;
@@ -736,7 +726,7 @@
         @Override
         public void run() {
             if (!mAppsToUpdate.isEmpty()) {
-                LauncherActivityInfoCompat app = mAppsToUpdate.pop();
+                LauncherActivityInfo app = mAppsToUpdate.pop();
                 String pkg = app.getComponentName().getPackageName();
                 PackageInfo info = mPkgInfoMap.get(pkg);
                 addIconToDBAndMemCache(app, info, mUserSerial, true /*replace existing*/);
@@ -744,14 +734,14 @@
 
                 if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) {
                     // No more app to update. Notify model.
-                    LauncherAppState.getInstance().getModel().onPackageIconsUpdated(
+                    LauncherAppState.getInstance(mContext).getModel().onPackageIconsUpdated(
                             mUpdatedPackages, mUserManager.getUserForSerialNumber(mUserSerial));
                 }
 
                 // Let it run one more time.
                 scheduleNext();
             } else if (!mAppsToAdd.isEmpty()) {
-                LauncherActivityInfoCompat app = mAppsToAdd.pop();
+                LauncherActivityInfo app = mAppsToAdd.pop();
                 PackageInfo info = mPkgInfoMap.get(app.getComponentName().getPackageName());
                 // We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every
                 // app should have package info, this is not guaranteed by the api
@@ -853,7 +843,7 @@
         }
     }
 
-    private class ActivityInfoProvider extends Provider<LauncherActivityInfoCompat> {
+    private class ActivityInfoProvider extends Provider<LauncherActivityInfo> {
 
         private final Intent mIntent;
         private final UserHandle mUser;
@@ -864,8 +854,16 @@
         }
 
         @Override
-        public LauncherActivityInfoCompat get() {
+        public LauncherActivityInfo get() {
             return mLauncherApps.resolveActivity(mIntent, mUser);
         }
     }
+
+    /**
+     * Interface for receiving itemInfo with high-res icon.
+     */
+    public interface ItemInfoUpdateReceiver {
+
+        void reapplyItemInfo(ItemInfoWithIcon info);
+    }
 }
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index 005bbaa..a5d3990 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -1,9 +1,8 @@
 package com.android.launcher3;
 
+import android.content.pm.LauncherActivityInfo;
 import android.graphics.drawable.Drawable;
 
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
-
 import java.util.Locale;
 
 public class IconProvider {
@@ -26,7 +25,7 @@
     }
 
 
-    public Drawable getIcon(LauncherActivityInfoCompat info, int iconDpi) {
+    public Drawable getIcon(LauncherActivityInfo info, int iconDpi) {
         return info.getIcon(iconDpi);
     }
 }
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index df2deb8..ad10523 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -23,18 +23,21 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
+import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.os.Looper;
+import android.os.Parcelable;
 import android.os.Process;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.Log;
 
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
@@ -238,7 +241,7 @@
 
     private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
         // Queue the item up for adding if launcher has not loaded properly yet
-        LauncherAppState app = LauncherAppState.getInstance();
+        LauncherAppState app = LauncherAppState.getInstance(context);
         boolean launcherNotLoaded = app.getModel().getCallback() == null;
 
         addToInstallQueue(Utilities.getPrefs(context), info);
@@ -258,7 +261,7 @@
     static void flushInstallQueue(Context context) {
         ArrayList<PendingInstallShortcutInfo> items = getAndClearInstallQueue(context);
         if (!items.isEmpty()) {
-            LauncherAppState.getInstance().getModel().addAndBindAddedWorkspaceItems(
+            LauncherAppState.getInstance(context).getModel().addAndBindAddedWorkspaceItems(
                     new LazyShortcutsProvider(context.getApplicationContext(), items));
         }
     }
@@ -282,7 +285,7 @@
 
     private static class PendingInstallShortcutInfo {
 
-        final LauncherActivityInfoCompat activityInfo;
+        final LauncherActivityInfo activityInfo;
         final ShortcutInfoCompat shortcutInfo;
         final AppWidgetProviderInfo providerInfo;
 
@@ -312,7 +315,7 @@
         /**
          * Initializes a PendingInstallShortcutInfo to represent a launcher target.
          */
-        public PendingInstallShortcutInfo(LauncherActivityInfoCompat info, Context context) {
+        public PendingInstallShortcutInfo(LauncherActivityInfo info, Context context) {
             activityInfo = info;
             shortcutInfo = null;
             providerInfo = null;
@@ -434,10 +437,25 @@
 
         public ItemInfo getItemInfo() {
             if (activityInfo != null) {
-                return new AppInfo(mContext, activityInfo, user,
-                        LauncherAppState.getInstance().getIconCache(),
-                        UserManagerCompat.getInstance(mContext).isQuietModeEnabled(user),
-                        false /* useLowResIcon */).makeShortcut();
+                AppInfo appInfo = new AppInfo(mContext, activityInfo, user);
+                final LauncherAppState app = LauncherAppState.getInstance(mContext);
+                // Set default values until proper values is loaded.
+                appInfo.title = "";
+                appInfo.iconBitmap = app.getIconCache().getDefaultIcon(user);
+                final ShortcutInfo si = appInfo.makeShortcut();
+                if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
+                    app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
+                } else {
+                    app.getModel().updateAndBindShortcutInfo(new Provider<ShortcutInfo>() {
+                        @Override
+                        public ShortcutInfo get() {
+                            app.getIconCache().getTitleAndIcon(
+                                    si, activityInfo, false /* useLowResIcon */);
+                            return si;
+                        }
+                    });
+                }
+                return si;
             } else if (shortcutInfo != null) {
                 return new ShortcutInfo(shortcutInfo, mContext);
             } else if (providerInfo != null) {
@@ -446,15 +464,14 @@
                 LauncherAppWidgetInfo widgetInfo = new LauncherAppWidgetInfo(
                         launchIntent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0),
                         info.provider);
-                InvariantDeviceProfile idp = LauncherAppState.getInstance()
-                        .getInvariantDeviceProfile();
+                InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
                 widgetInfo.minSpanX = info.minSpanX;
                 widgetInfo.minSpanY = info.minSpanY;
                 widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
                 widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
                 return widgetInfo;
             } else {
-                return LauncherAppState.getInstance().getModel().infoFromShortcutIntent(mContext, data);
+                return createShortcutInfo(data, LauncherAppState.getInstance(mContext));
             }
         }
 
@@ -472,7 +489,7 @@
         try {
             Decoder decoder = new Decoder(encoded, context);
             if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
-                LauncherActivityInfoCompat info = LauncherAppsCompat.getInstance(context)
+                LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
                         .resolveActivity(decoder.launcherIntent, decoder.user);
                 return info == null ? null : new PendingInstallShortcutInfo(info, context);
             } else if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
@@ -554,7 +571,7 @@
             return original;
         }
 
-        LauncherActivityInfoCompat info = LauncherAppsCompat.getInstance(original.mContext)
+        LauncherActivityInfo info = LauncherAppsCompat.getInstance(original.mContext)
                 .resolveActivity(original.launchIntent, original.user);
         if (info == null) {
             return original;
@@ -598,4 +615,42 @@
             return installQueue;
         }
     }
+
+    private static ShortcutInfo createShortcutInfo(Intent data, LauncherAppState app) {
+        Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
+        String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
+        Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
+
+        if (intent == null) {
+            // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
+            Log.e(TAG, "Can't construct ShorcutInfo with null intent");
+            return null;
+        }
+
+        final ShortcutInfo info = new ShortcutInfo();
+
+        // Only support intents for current user for now. Intents sent from other
+        // users wouldn't get here without intent forwarding anyway.
+        info.user = Process.myUserHandle();
+
+        if (bitmap instanceof Bitmap) {
+            info.iconBitmap = LauncherIcons.createIconBitmap((Bitmap) bitmap, app.getContext());
+        } else {
+            Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
+            if (extra instanceof Intent.ShortcutIconResource) {
+                info.iconResource = (Intent.ShortcutIconResource) extra;
+                info.iconBitmap = LauncherIcons.createIconBitmap(info.iconResource, app.getContext());
+            }
+        }
+        if (info.iconBitmap == null) {
+            info.iconBitmap = app.getIconCache().getDefaultIcon(info.user);
+        }
+
+        info.title = Utilities.trim(name);
+        info.contentDescription = UserManagerCompat.getInstance(app.getContext())
+                .getBadgedLabelForUser(info.title, info.user);
+        info.intent = intent;
+        return info;
+    }
+
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8e28912..8322f66 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -356,7 +356,7 @@
 
         super.onCreate(savedInstanceState);
 
-        LauncherAppState app = LauncherAppState.getInstance();
+        LauncherAppState app = LauncherAppState.getInstance(this);
 
         // Load configuration-specific DeviceProfile
         mDeviceProfile =
@@ -1815,7 +1815,7 @@
         // been created. In this case, don't interfere with the new Launcher.
         if (mModel.isCurrentCallbacks(this)) {
             mModel.stopLoader();
-            LauncherAppState.getInstance().setLauncher(null);
+            LauncherAppState.getInstance(this).setLauncher(null);
         }
 
         if (mRotationPrefChangeHandler != null) {
@@ -2337,13 +2337,14 @@
             showBrokenAppInstallDialog(packageName,
                 new DialogInterface.OnClickListener() {
                     public void onClick(DialogInterface dialog, int id) {
-                        startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
+                        startActivitySafely(
+                                v, PackageManagerHelper.getMarketIntent(packageName), info);
                     }
                 });
         } else {
             // Download has started.
             final String packageName = info.providerName.getPackageName();
-            startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
+            startActivitySafely(v, PackageManagerHelper.getMarketIntent(packageName), info);
         }
     }
 
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 00e4bf4..4dc5a97 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -48,7 +48,7 @@
 
     private InvariantDeviceProfile mInvariantDeviceProfile;
 
-    public static LauncherAppState getInstance() {
+    public static LauncherAppState getInstance(Context context) {
         if (INSTANCE == null) {
             INSTANCE = new LauncherAppState();
         }
@@ -159,4 +159,11 @@
     public InvariantDeviceProfile getInvariantDeviceProfile() {
         return mInvariantDeviceProfile;
     }
+
+    /**
+     * Shorthand for {@link #getInvariantDeviceProfile()}
+     */
+    public static InvariantDeviceProfile getIDP(Context context) {
+        return LauncherAppState.getInstance(context).getInvariantDeviceProfile();
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 49bbfd0..1429df5 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -33,9 +33,11 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.AdapterView;
 import android.widget.Advanceable;
 import android.widget.RemoteViews;
 
+import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
 
 import java.lang.reflect.Method;
@@ -45,7 +47,8 @@
 /**
  * {@inheritDoc}
  */
-public class LauncherAppWidgetHostView extends AppWidgetHostView implements TouchCompleteListener {
+public class LauncherAppWidgetHostView extends AppWidgetHostView
+        implements TouchCompleteListener, View.OnLongClickListener {
 
     private static final String TAG = "LauncherWidgetHostView";
 
@@ -56,11 +59,12 @@
     // Maintains a list of widget ids which are supposed to be auto advanced.
     private static final SparseBooleanArray sAutoAdvanceWidgetIds = new SparseBooleanArray();
 
-    LayoutInflater mInflater;
+    protected final LayoutInflater mInflater;
 
-    private CheckLongPressHelper mLongPressHelper;
-    private StylusEventHelper mStylusEventHelper;
-    private Context mContext;
+    private final CheckLongPressHelper mLongPressHelper;
+    private final StylusEventHelper mStylusEventHelper;
+    private final Context mContext;
+
     @ViewDebug.ExportedProperty(category = "launcher")
     private int mPreviousOrientation;
 
@@ -69,6 +73,7 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mChildrenFocused;
 
+    private boolean mIsScrollable;
     private boolean mIsAttachedToWindow;
     private boolean mIsAutoAdvanceRegistered;
     private Runnable mAutoAdvanceRunnable;
@@ -86,7 +91,7 @@
     public LauncherAppWidgetHostView(Context context) {
         super(context);
         mContext = context;
-        mLongPressHelper = new CheckLongPressHelper(this);
+        mLongPressHelper = new CheckLongPressHelper(this, this);
         mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
         mInflater = LayoutInflater.from(context);
         setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate());
@@ -104,6 +109,16 @@
     }
 
     @Override
+    public boolean onLongClick(View view) {
+        if (mIsScrollable) {
+            DragLayer dragLayer = Launcher.getLauncher(getContext()).getDragLayer();
+            dragLayer.requestDisallowInterceptTouchEvent(false);
+        }
+        view.performLongClick();
+        return true;
+    }
+
+    @Override
     protected View getErrorView() {
         return mInflater.inflate(R.layout.appwidget_error, this, false);
     }
@@ -120,6 +135,24 @@
 
         // The provider info or the views might have changed.
         checkIfAutoAdvance();
+
+        mIsScrollable = checkScrollableRecursively(this);
+    }
+
+    private boolean checkScrollableRecursively(ViewGroup viewGroup) {
+        if (viewGroup instanceof AdapterView) {
+            return true;
+        } else {
+            for (int i=0; i < viewGroup.getChildCount(); i++) {
+                View child = viewGroup.getChildAt(i);
+                if (child instanceof ViewGroup) {
+                    if (checkScrollableRecursively((ViewGroup) child)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
     }
 
     public boolean isReinflateRequired() {
@@ -150,12 +183,18 @@
             mLongPressHelper.cancelLongPress();
             return true;
         }
+
         switch (ev.getAction()) {
             case MotionEvent.ACTION_DOWN: {
+                DragLayer dragLayer = Launcher.getLauncher(getContext()).getDragLayer();
+
+                if (mIsScrollable) {
+                     dragLayer.requestDisallowInterceptTouchEvent(true);
+                }
                 if (!mStylusEventHelper.inStylusButtonPressed()) {
                     mLongPressHelper.postCheckForLongPress();
                 }
-                Launcher.getLauncher(getContext()).getDragLayer().setTouchCompleteListener(this);
+                dragLayer.setTouchCompleteListener(this);
                 break;
             }
 
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index ab8f395..6cb703b 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -65,7 +65,7 @@
     }
 
     public void initSpans(Context context) {
-        InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+        InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
 
         Point paddingLand = idp.landscapeProfile.getTotalWorkspacePadding();
         Point paddingPort = idp.portraitProfile.getTotalWorkspacePadding();
@@ -107,8 +107,7 @@
         if (isCustomWidget) {
             return cache.getFullResIcon(provider.getPackageName(), icon);
         }
-        return super.loadIcon(context,
-                LauncherAppState.getInstance().getInvariantDeviceProfile().fillResIconDpi);
+        return super.loadIcon(context, LauncherAppState.getIDP(context).fillResIconDpi);
     }
 
     public String toString(PackageManager pm) {
diff --git a/src/com/android/launcher3/LauncherBackupAgent.java b/src/com/android/launcher3/LauncherBackupAgent.java
index b3e73f7..140794b 100644
--- a/src/com/android/launcher3/LauncherBackupAgent.java
+++ b/src/com/android/launcher3/LauncherBackupAgent.java
@@ -5,11 +5,19 @@
 import android.app.backup.BackupDataOutput;
 import android.os.ParcelFileDescriptor;
 
+import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.provider.RestoreDbTask;
 
 public class LauncherBackupAgent extends BackupAgent {
 
     @Override
+    public void onCreate() {
+        super.onCreate();
+        // Set the log dir as LauncherAppState is not initialized during restore.
+        FileLog.setDir(getFilesDir());
+    }
+
+    @Override
     public void onRestore(
             BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) {
         // Doesn't do incremental backup/restore
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 58a7495..0907c8c 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -24,44 +24,38 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.content.Intent.ShortcutIconResource;
 import android.content.IntentFilter;
+import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
-import android.os.Parcelable;
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.provider.BaseColumns;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.MutableInt;
 
 import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.dynamicui.ExtractionUtils;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.AddWorkspaceItemsTask;
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.CacheDataUpdatedTask;
 import com.android.launcher3.model.ExtendedModelTask;
 import com.android.launcher3.model.GridSizeMigrationTask;
+import com.android.launcher3.model.LoaderCursor;
 import com.android.launcher3.model.PackageInstallStateChangedTask;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.model.PackageUpdatedTask;
@@ -77,10 +71,7 @@
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.CursorIconInfo;
-import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.ManagedProfileHeuristic;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -91,7 +82,6 @@
 
 import java.lang.ref.WeakReference;
 import java.net.URISyntaxException;
-import java.security.InvalidParameterException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -346,9 +336,10 @@
         final ContentResolver cr = context.getContentResolver();
 
         final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
+        final Context appContext = context.getApplicationContext();
         Runnable r = new Runnable() {
             public void run() {
-                cr.update(uri, writer.getValues(), null, null);
+                cr.update(uri, writer.getValues(appContext), null, null);
                 updateItemArrays(item, itemId, stackTrace);
             }
         };
@@ -563,13 +554,14 @@
         writer.put(LauncherSettings.Favorites._ID, item.id);
 
         final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
+        final Context appContext = context.getApplicationContext();
         Runnable r = new Runnable() {
             public void run() {
-                cr.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues());
+                cr.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(appContext));
 
                 synchronized (sBgDataModel) {
                     checkItemInfoLocked(item.id, item, stackTrace);
-                    sBgDataModel.addItem(item, true);
+                    sBgDataModel.addItem(appContext, item, true);
                 }
             }
         };
@@ -598,13 +590,14 @@
     public static void deleteItemsFromDatabase(Context context,
             final Iterable<? extends ItemInfo> items) {
         final ContentResolver cr = context.getContentResolver();
+        final Context appContext = context.getApplicationContext();
         Runnable r = new Runnable() {
             public void run() {
                 for (ItemInfo item : items) {
                     final Uri uri = LauncherSettings.Favorites.getContentUri(item.id);
                     cr.delete(uri, null, null);
 
-                    sBgDataModel.removeItem(item);
+                    sBgDataModel.removeItem(appContext, item);
                 }
             }
         };
@@ -664,16 +657,17 @@
      */
     public static void deleteFolderAndContentsFromDatabase(Context context, final FolderInfo info) {
         final ContentResolver cr = context.getContentResolver();
+        final Context appContext = context.getApplicationContext();
 
         Runnable r = new Runnable() {
             public void run() {
                 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
                         LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
-                sBgDataModel.removeItem(info.contents);
+                sBgDataModel.removeItem(appContext, info.contents);
                 info.contents.clear();
 
                 cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null);
-                sBgDataModel.removeItem(info);
+                sBgDataModel.removeItem(appContext, info);
             }
         };
         runOnWorkerThread(r);
@@ -1096,96 +1090,6 @@
             }
         }
 
-        // check & update map of what's occupied; used to discard overlapping/invalid items
-        private boolean checkItemPlacement(LongArrayMap<GridOccupancy> occupied, ItemInfo item,
-                   ArrayList<Long> workspaceScreens) {
-            LauncherAppState app = LauncherAppState.getInstance();
-            InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
-
-            long containerIndex = item.screenId;
-            if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-                // Return early if we detect that an item is under the hotseat button
-                if (!FeatureFlags.NO_ALL_APPS_ICON &&
-                        profile.isAllAppsButtonRank((int) item.screenId)) {
-                    Log.e(TAG, "Error loading shortcut into hotseat " + item
-                            + " into position (" + item.screenId + ":" + item.cellX + ","
-                            + item.cellY + ") occupied by all apps");
-                    return false;
-                }
-
-                final GridOccupancy hotseatOccupancy =
-                        occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
-
-                if (item.screenId >= profile.numHotseatIcons) {
-                    Log.e(TAG, "Error loading shortcut " + item
-                            + " into hotseat position " + item.screenId
-                            + ", position out of bounds: (0 to " + (profile.numHotseatIcons - 1)
-                            + ")");
-                    return false;
-                }
-
-                if (hotseatOccupancy != null) {
-                    if (hotseatOccupancy.cells[(int) item.screenId][0]) {
-                        Log.e(TAG, "Error loading shortcut into hotseat " + item
-                                + " into position (" + item.screenId + ":" + item.cellX + ","
-                                + item.cellY + ") already occupied");
-                            return false;
-                    } else {
-                        hotseatOccupancy.cells[(int) item.screenId][0] = true;
-                        return true;
-                    }
-                } else {
-                    final GridOccupancy occupancy = new GridOccupancy(profile.numHotseatIcons, 1);
-                    occupancy.cells[(int) item.screenId][0] = true;
-                    occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
-                    return true;
-                }
-            } else if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                if (!workspaceScreens.contains((Long) item.screenId)) {
-                    // The item has an invalid screen id.
-                    return false;
-                }
-            } else {
-                // Skip further checking if it is not the hotseat or workspace container
-                return true;
-            }
-
-            final int countX = profile.numColumns;
-            final int countY = profile.numRows;
-            if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
-                    item.cellX < 0 || item.cellY < 0 ||
-                    item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
-                Log.e(TAG, "Error loading shortcut " + item
-                        + " into cell (" + containerIndex + "-" + item.screenId + ":"
-                        + item.cellX + "," + item.cellY
-                        + ") out of screen bounds ( " + countX + "x" + countY + ")");
-                return false;
-            }
-
-            if (!occupied.containsKey(item.screenId)) {
-                GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1);
-                if (item.screenId == Workspace.FIRST_SCREEN_ID) {
-                    // Mark the first row as occupied (if the feature is enabled)
-                    // in order to account for the QSB.
-                    screen.markCells(0, 0, countX + 1, 1, FeatureFlags.QSB_ON_FIRST_SCREEN);
-                }
-                occupied.put(item.screenId, screen);
-            }
-            final GridOccupancy occupancy = occupied.get(item.screenId);
-
-            // Check if any workspace icons overlap with each other
-            if (occupancy.isRegionVacant(item.cellX, item.cellY, item.spanX, item.spanY)) {
-                occupancy.markCells(item, true);
-                return true;
-            } else {
-                Log.e(TAG, "Error loading shortcut " + item
-                        + " into cell (" + containerIndex + "-" + item.screenId + ":"
-                        + item.cellX + "," + item.cellX + "," + item.spanX + "," + item.spanY
-                        + ") already occupied");
-                return false;
-            }
-        }
-
         private void loadWorkspace() {
             if (LauncherAppState.PROFILE_STARTUP) {
                 Trace.beginSection("Loading Workspace");
@@ -1201,8 +1105,7 @@
             final boolean isSdCardReady = Utilities.isBootCompleted();
             final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>();
 
-            LauncherAppState app = LauncherAppState.getInstance();
-            InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+            InvariantDeviceProfile profile = mApp.getInvariantDeviceProfile();
             int countX = profile.numColumns;
             int countY = profile.numRows;
 
@@ -1237,37 +1140,19 @@
                         .getInstance(mContext).updateAndGetActiveSessionCache();
                 sBgDataModel.workspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
 
-                final ArrayList<Long> itemsToRemove = new ArrayList<>();
-                final ArrayList<Long> restoredRows = new ArrayList<>();
                 Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
-                final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
-                if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
-                final Cursor c = contentResolver.query(contentUri, null, null, null, null);
+                final LoaderCursor c = new LoaderCursor(contentResolver.query(
+                        LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
 
-                // +1 for the hotseat (it can be larger than the workspace)
-                // Load workspace in reverse order to ensure that latest items are loaded first (and
-                // before any earlier duplicates)
-                final LongArrayMap<GridOccupancy> occupied = new LongArrayMap<>();
                 HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
 
                 try {
-                    final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
                     final int intentIndex = c.getColumnIndexOrThrow
                             (LauncherSettings.Favorites.INTENT);
-                    final int containerIndex = c.getColumnIndexOrThrow(
-                            LauncherSettings.Favorites.CONTAINER);
-                    final int itemTypeIndex = c.getColumnIndexOrThrow(
-                            LauncherSettings.Favorites.ITEM_TYPE);
                     final int appWidgetIdIndex = c.getColumnIndexOrThrow(
                             LauncherSettings.Favorites.APPWIDGET_ID);
                     final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
                             LauncherSettings.Favorites.APPWIDGET_PROVIDER);
-                    final int screenIndex = c.getColumnIndexOrThrow(
-                            LauncherSettings.Favorites.SCREEN);
-                    final int cellXIndex = c.getColumnIndexOrThrow
-                            (LauncherSettings.Favorites.CELLX);
-                    final int cellYIndex = c.getColumnIndexOrThrow
-                            (LauncherSettings.Favorites.CELLY);
                     final int spanXIndex = c.getColumnIndexOrThrow
                             (LauncherSettings.Favorites.SPANX);
                     final int spanYIndex = c.getColumnIndexOrThrow(
@@ -1276,13 +1161,10 @@
                             LauncherSettings.Favorites.RANK);
                     final int restoredIndex = c.getColumnIndexOrThrow(
                             LauncherSettings.Favorites.RESTORED);
-                    final int profileIdIndex = c.getColumnIndexOrThrow(
-                            LauncherSettings.Favorites.PROFILE_ID);
                     final int optionsIndex = c.getColumnIndexOrThrow(
                             LauncherSettings.Favorites.OPTIONS);
-                    final CursorIconInfo cursorIconInfo = new CursorIconInfo(mContext, c);
 
-                    final LongSparseArray<UserHandle> allUsers = new LongSparseArray<>();
+                    final LongSparseArray<UserHandle> allUsers = c.allUsers;
                     final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
                     final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
                     for (UserHandle user : mUserManager.getUserProfiles()) {
@@ -1314,45 +1196,42 @@
                     ShortcutInfo info;
                     String intentDescription;
                     LauncherAppWidgetInfo appWidgetInfo;
-                    int container;
-                    long id;
-                    long serialNumber;
                     Intent intent;
-                    UserHandle user;
                     String targetPackage;
 
                     while (!mStopped && c.moveToNext()) {
                         try {
-                            int itemType = c.getInt(itemTypeIndex);
+                            if (c.user == null) {
+                                // User has been deleted, remove the item.
+                                c.markDeleted("User has been deleted");
+                                continue;
+                            }
+
                             boolean restored = 0 != c.getInt(restoredIndex);
                             boolean allowMissingTarget = false;
-                            container = c.getInt(containerIndex);
-
-                            switch (itemType) {
-                            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-                            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                                id = c.getLong(idIndex);
-                                intentDescription = c.getString(intentIndex);
-                                serialNumber = c.getInt(profileIdIndex);
-                                user = allUsers.get(serialNumber);
-                                int promiseType = c.getInt(restoredIndex);
-                                int disabledState = 0;
-                                boolean itemReplaced = false;
-                                targetPackage = null;
-                                if (user == null) {
-                                    // User has been deleted remove the item.
-                                    itemsToRemove.add(id);
+                            switch (c.itemType) {
+                            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: {
+                                if (!Process.myUserHandle().equals(c.user)) {
+                                    c.markDeleted("Legacy shortcuts are only allowed for default user");
                                     continue;
                                 }
+                                // Follow through.
+                            }
+                            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+                            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+                                intentDescription = c.getString(intentIndex);
+                                int promiseType = c.getInt(restoredIndex);
+                                int disabledState = 0;
+                                targetPackage = null;
+
                                 try {
                                     intent = Intent.parseUri(intentDescription, 0);
                                     ComponentName cn = intent.getComponent();
                                     if (cn != null && cn.getPackageName() != null) {
                                         boolean validPkg = launcherApps.isPackageEnabledForProfile(
-                                                cn.getPackageName(), user);
+                                                cn.getPackageName(), c.user);
                                         boolean validComponent = validPkg &&
-                                                launcherApps.isActivityEnabledForProfile(cn, user);
+                                                launcherApps.isActivityEnabledForProfile(cn, c.user);
                                         if (validPkg) {
                                             targetPackage = cn.getPackageName();
                                         }
@@ -1360,10 +1239,10 @@
                                         if (validComponent) {
                                             if (restored) {
                                                 // no special handling necessary for this item
-                                                restoredRows.add(id);
+                                                c.markRestored();
                                                 restored = false;
                                             }
-                                            if (quietMode.get(serialNumber)) {
+                                            if (quietMode.get(c.serialNumber)) {
                                                 disabledState = ShortcutInfo.FLAG_DISABLED_QUIET_USER;
                                             }
                                         } else if (validPkg) {
@@ -1374,22 +1253,20 @@
                                                 intent = manager.getLaunchIntentForPackage(
                                                         cn.getPackageName());
                                                 if (intent != null) {
-                                                    ContentValues values = new ContentValues();
-                                                    values.put(LauncherSettings.Favorites.INTENT,
-                                                            intent.toUri(0));
-                                                    updateItem(id, values);
+                                                    c.updater().put(
+                                                            LauncherSettings.Favorites.INTENT,
+                                                            intent.toUri(0)).commit();
                                                 }
                                             }
 
                                             if (intent == null) {
                                                 // The app is installed but the component is no
                                                 // longer available.
-                                                FileLog.d(TAG, "Invalid component removed: " + cn);
-                                                itemsToRemove.add(id);
+                                                c.markDeleted("Invalid component removed: " + cn);
                                                 continue;
                                             } else {
                                                 // no special handling necessary for this item
-                                                restoredRows.add(id);
+                                                c.markRestored();
                                                 restored = false;
                                             }
                                         } else if (restored) {
@@ -1402,32 +1279,11 @@
                                             } else if (installingPkgs.containsKey(cn.getPackageName())) {
                                                 // App restore has started. Update the flag
                                                 promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED;
-                                                ContentValues values = new ContentValues();
-                                                values.put(LauncherSettings.Favorites.RESTORED,
-                                                        promiseType);
-                                                updateItem(id, values);
-                                            } else if ((promiseType & ShortcutInfo.FLAG_RESTORED_APP_TYPE) != 0) {
-                                                // This is a common app. Try to replace this.
-                                                int appType = CommonAppTypeParser.decodeItemTypeFromFlag(promiseType);
-                                                CommonAppTypeParser parser = new CommonAppTypeParser(id, appType, context);
-                                                if (parser.findDefaultApp()) {
-                                                    // Default app found. Replace it.
-                                                    intent = parser.parsedIntent;
-                                                    cn = intent.getComponent();
-                                                    ContentValues values = parser.parsedValues;
-                                                    values.put(LauncherSettings.Favorites.RESTORED, 0);
-                                                    updateItem(id, values);
-                                                    restored = false;
-                                                    itemReplaced = true;
-
-                                                } else {
-                                                    FileLog.d(TAG, "Unrestored package removed: " + cn);
-                                                    itemsToRemove.add(id);
-                                                    continue;
-                                                }
+                                                c.updater().put(
+                                                        LauncherSettings.Favorites.RESTORED,
+                                                        promiseType).commit();
                                             } else {
-                                                FileLog.d(TAG, "Unrestored package removed: " + cn);
-                                                itemsToRemove.add(id);
+                                                c.markDeleted("Unrestored package removed: " + cn);
                                                 continue;
                                             }
                                         } else if (PackageManagerHelper.isAppOnSdcard(
@@ -1439,79 +1295,64 @@
                                             // SdCard is not ready yet. Package might get available,
                                             // once it is ready.
                                             Log.d(TAG, "Invalid package: " + cn + " (check again later)");
-                                            pendingPackages.addToList(user, cn.getPackageName());
+                                            pendingPackages.addToList(c.user, cn.getPackageName());
                                             allowMissingTarget = true;
                                             // Add the icon on the workspace anyway.
 
                                         } else {
                                             // Do not wait for external media load anymore.
                                             // Log the invalid package, and remove it
-                                            FileLog.d(TAG, "Invalid package removed: " + cn);
-                                            itemsToRemove.add(id);
+                                            c.markDeleted("Invalid package removed: " + cn);
                                             continue;
                                         }
                                     } else if (cn == null) {
                                         // For shortcuts with no component, keep them as they are
-                                        restoredRows.add(id);
+                                        c.markRestored();
                                         restored = false;
                                     }
                                 } catch (URISyntaxException e) {
-                                    FileLog.d(TAG, "Invalid uri: " + intentDescription);
-                                    itemsToRemove.add(id);
+                                    c.markDeleted("Invalid uri: " + intentDescription);
                                     continue;
                                 }
 
-                                boolean useLowResIcon = container >= 0 &&
+                                boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
                                         c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
 
-                                if (itemReplaced) {
-                                    if (user.equals(Process.myUserHandle())) {
-                                        info = getAppShortcutInfo(intent, user, null,
-                                                cursorIconInfo, false, useLowResIcon);
-                                    } else {
-                                        // Don't replace items for other profiles.
-                                        itemsToRemove.add(id);
-                                        continue;
-                                    }
-                                } else if (restored) {
-                                    if (user.equals(Process.myUserHandle())) {
-                                        info = getRestoredItemInfo(c, intent,
-                                                promiseType, itemType, cursorIconInfo);
-                                        intent = getRestoredItemIntent(c, context, intent);
+                                if (restored) {
+                                    if (c.user.equals(Process.myUserHandle())) {
+                                        info = c.getRestoredItemInfo(intent, promiseType);
+                                        intent = PackageManagerHelper.getMarketIntent(
+                                                intent.getComponent().getPackageName());
                                     } else {
                                         // Don't restore items for other profiles.
-                                        itemsToRemove.add(id);
+                                        c.markDeleted("Restore from managed profile not supported");
                                         continue;
                                     }
-                                } else if (itemType ==
+                                } else if (c.itemType ==
                                         LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-                                    info = getAppShortcutInfo(intent, user, c,
-                                            cursorIconInfo, allowMissingTarget, useLowResIcon);
-                                } else if (itemType ==
+                                    info = c.getAppShortcutInfo(
+                                            intent, allowMissingTarget, useLowResIcon);
+                                } else if (c.itemType ==
                                         LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
 
-                                    ShortcutKey key = ShortcutKey.fromIntent(intent, user);
-                                    if (unlockedUsers.get(serialNumber)) {
+                                    ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
+                                    if (unlockedUsers.get(c.serialNumber)) {
                                         ShortcutInfoCompat pinnedShortcut =
                                                 shortcutKeyToPinnedShortcuts.get(key);
                                         if (pinnedShortcut == null) {
                                             // The shortcut is no longer valid.
-                                            itemsToRemove.add(id);
+                                            c.markDeleted("Pinned shortcut not found");
                                             continue;
                                         }
                                         info = new ShortcutInfo(pinnedShortcut, context);
                                         intent = info.intent;
                                     } else {
                                         // Create a shortcut info in disabled mode for now.
-                                        info = new ShortcutInfo();
-                                        info.user = user;
-                                        info.itemType = itemType;
-                                        loadInfoFromCursor(info, c, cursorIconInfo);
-
+                                        info = c.loadSimpleShortcut();
                                         info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
                                     }
                                 } else { // item type == ITEM_TYPE_SHORTCUT
-                                    info = getShortcutInfo(c, cursorIconInfo);
+                                    info = c.loadSimpleShortcut();
 
                                     // Shortcuts are only available on the primary profile
                                     if (PackageManagerHelper.isAppSuspended(manager, targetPackage)) {
@@ -1532,30 +1373,23 @@
                                 }
 
                                 if (info != null) {
-                                    info.id = id;
+                                    c.applyCommonProperties(info);
+
                                     info.intent = intent;
-                                    info.container = container;
-                                    info.screenId = c.getInt(screenIndex);
-                                    info.cellX = c.getInt(cellXIndex);
-                                    info.cellY = c.getInt(cellYIndex);
                                     info.rank = c.getInt(rankIndex);
                                     info.spanX = 1;
                                     info.spanY = 1;
-                                    info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
+                                    // TODO: Remove this extra. Instead we should be using
+                                    // itemInfo#user.
+                                    info.intent.putExtra(ItemInfo.EXTRA_PROFILE, c.serialNumber);
                                     if (info.promisedIntent != null) {
-                                        info.promisedIntent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
+                                        info.promisedIntent.putExtra(ItemInfo.EXTRA_PROFILE, c.serialNumber);
                                     }
                                     info.isDisabled |= disabledState;
                                     if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
                                         info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
                                     }
 
-                                    // check & update map of what's occupied
-                                    if (!checkItemPlacement(occupied, info, sBgDataModel.workspaceScreens)) {
-                                        itemsToRemove.add(id);
-                                        break;
-                                    }
-
                                     if (restored) {
                                         ComponentName cn = info.getTargetComponent();
                                         if (cn != null) {
@@ -1568,55 +1402,38 @@
                                         }
                                     }
 
-                                    sBgDataModel.addItem(info, false);
+                                    c.checkAndAddItem(info, sBgDataModel);
                                 } else {
                                     throw new RuntimeException("Unexpected null ShortcutInfo");
                                 }
                                 break;
 
                             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
-                                id = c.getLong(idIndex);
-                                FolderInfo folderInfo = sBgDataModel.findOrMakeFolder(id);
+                                FolderInfo folderInfo = sBgDataModel.findOrMakeFolder(c.id);
+                                c.applyCommonProperties(folderInfo);
 
                                 // Do not trim the folder label, as is was set by the user.
-                                folderInfo.title = c.getString(cursorIconInfo.titleIndex);
-                                folderInfo.id = id;
-                                folderInfo.container = container;
-                                folderInfo.screenId = c.getInt(screenIndex);
-                                folderInfo.cellX = c.getInt(cellXIndex);
-                                folderInfo.cellY = c.getInt(cellYIndex);
+                                folderInfo.title = c.getString(c.titleIndex);
                                 folderInfo.spanX = 1;
                                 folderInfo.spanY = 1;
                                 folderInfo.options = c.getInt(optionsIndex);
 
-                                // check & update map of what's occupied
-                                if (!checkItemPlacement(occupied, folderInfo, sBgDataModel.workspaceScreens)) {
-                                    itemsToRemove.add(id);
-                                    break;
-                                }
                                 if (restored) {
                                     // no special handling required for restored folders
-                                    restoredRows.add(id);
+                                    c.markRestored();
                                 }
 
-                                sBgDataModel.addItem(folderInfo, false);
+                                c.checkAndAddItem(folderInfo, sBgDataModel);
                                 break;
 
                             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                             case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
                                 // Read all Launcher-specific widget details
-                                boolean customWidget = itemType ==
+                                boolean customWidget = c.itemType ==
                                     LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
 
                                 int appWidgetId = c.getInt(appWidgetIdIndex);
-                                serialNumber = c.getLong(profileIdIndex);
                                 String savedProvider = c.getString(appWidgetProviderIndex);
-                                id = c.getLong(idIndex);
-                                user = allUsers.get(serialNumber);
-                                if (user == null) {
-                                    itemsToRemove.add(id);
-                                    continue;
-                                }
 
                                 final ComponentName component =
                                         ComponentName.unflattenFromString(savedProvider);
@@ -1634,14 +1451,14 @@
                                 final AppWidgetProviderInfo provider = widgetProvidersMap.get(
                                         new ComponentKey(
                                                 ComponentName.unflattenFromString(savedProvider),
-                                                user));
+                                                c.user));
 
                                 final boolean isProviderReady = isValidProvider(provider);
                                 if (!isSafeMode && !customWidget &&
                                         wasProviderReady && !isProviderReady) {
-                                    FileLog.d(TAG, "Deleting widget that isn't installed anymore: "
+                                    c.markDeleted(
+                                            "Deleting widget that isn't installed anymore: "
                                             + provider);
-                                    itemsToRemove.add(id);
                                 } else {
                                     if (isProviderReady) {
                                         appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
@@ -1666,7 +1483,7 @@
                                         }
                                         appWidgetInfo.restoreStatus = status;
                                     } else {
-                                        Log.v(TAG, "Widget restore pending id=" + id
+                                        Log.v(TAG, "Widget restore pending id=" + c.id
                                                 + " appWidgetId=" + appWidgetId
                                                 + " status =" + restoreStatus);
                                         appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
@@ -1681,8 +1498,7 @@
                                             appWidgetInfo.restoreStatus |=
                                                     LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
                                         } else if (!isSafeMode) {
-                                            FileLog.d(TAG, "Unrestored widget removed: " + component);
-                                            itemsToRemove.add(id);
+                                            c.markDeleted("Unrestored widget removed: " + component);
                                             continue;
                                         }
 
@@ -1698,44 +1514,31 @@
                                         }
                                     }
 
-                                    appWidgetInfo.id = id;
-                                    appWidgetInfo.screenId = c.getInt(screenIndex);
-                                    appWidgetInfo.cellX = c.getInt(cellXIndex);
-                                    appWidgetInfo.cellY = c.getInt(cellYIndex);
+                                    c.applyCommonProperties(appWidgetInfo);
                                     appWidgetInfo.spanX = c.getInt(spanXIndex);
                                     appWidgetInfo.spanY = c.getInt(spanYIndex);
-                                    appWidgetInfo.user = user;
+                                    appWidgetInfo.user = c.user;
 
-                                    if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
-                                        container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-                                        Log.e(TAG, "Widget found where container != " +
+                                    if (!c.isOnWorkspaceOrHotseat()) {
+                                        c.markDeleted("Widget found where container != " +
                                                 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
-                                        itemsToRemove.add(id);
                                         continue;
                                     }
 
-                                    appWidgetInfo.container = container;
-                                    // check & update map of what's occupied
-                                    if (!checkItemPlacement(occupied, appWidgetInfo, sBgDataModel.workspaceScreens)) {
-                                        itemsToRemove.add(id);
-                                        break;
-                                    }
-
                                     if (!customWidget) {
                                         String providerName =
                                                 appWidgetInfo.providerName.flattenToString();
                                         if (!providerName.equals(savedProvider) ||
                                                 (appWidgetInfo.restoreStatus != restoreStatus)) {
-                                            ContentValues values = new ContentValues();
-                                            values.put(
-                                                    LauncherSettings.Favorites.APPWIDGET_PROVIDER,
-                                                    providerName);
-                                            values.put(LauncherSettings.Favorites.RESTORED,
-                                                    appWidgetInfo.restoreStatus);
-                                            updateItem(id, values);
+                                            c.updater()
+                                                    .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
+                                                            providerName)
+                                                    .put(LauncherSettings.Favorites.RESTORED,
+                                                            appWidgetInfo.restoreStatus)
+                                                    .commit();
                                         }
                                     }
-                                    sBgDataModel.addItem(appWidgetInfo, false);
+                                    c.checkAndAddItem(appWidgetInfo, sBgDataModel);
                                 }
                                 break;
                             }
@@ -1753,16 +1556,8 @@
                     return;
                 }
 
-                if (itemsToRemove.size() > 0) {
-                    // Remove dead items
-                    contentResolver.delete(LauncherSettings.Favorites.CONTENT_URI,
-                            Utilities.createDbSelectionQuery(
-                                    LauncherSettings.Favorites._ID, itemsToRemove), null);
-                    if (DEBUG_LOADERS) {
-                        Log.d(TAG, "Removed = " + Utilities.createDbSelectionQuery(
-                                LauncherSettings.Favorites._ID, itemsToRemove));
-                    }
-
+                // Remove dead items
+                if (c.commitDeleted()) {
                     // Remove any empty folder
                     ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
                             .call(contentResolver,
@@ -1803,15 +1598,7 @@
                     }
                 }
 
-                if (restoredRows.size() > 0) {
-                    // Update restored items that no longer require special handling
-                    ContentValues values = new ContentValues();
-                    values.put(LauncherSettings.Favorites.RESTORED, 0);
-                    contentResolver.update(LauncherSettings.Favorites.CONTENT_URI, values,
-                            Utilities.createDbSelectionQuery(
-                                    LauncherSettings.Favorites._ID, restoredRows), null);
-                }
-
+                c.commitRestoredItems();
                 if (!isSdCardReady && !pendingPackages.isEmpty()) {
                     context.registerReceiver(
                             new SdCardAvailableReceiver(
@@ -1822,7 +1609,7 @@
                 }
 
                 // Remove any empty screens
-                ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgDataModel.workspaceScreens);
+                ArrayList<Long> unusedScreens = new ArrayList<>(sBgDataModel.workspaceScreens);
                 for (ItemInfo item: sBgDataModel.itemsIdMap) {
                     long screenId = item.screenId;
                     if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
@@ -1836,40 +1623,12 @@
                     sBgDataModel.workspaceScreens.removeAll(unusedScreens);
                     updateWorkspaceScreenOrder(context, sBgDataModel.workspaceScreens);
                 }
-
-                if (DEBUG_LOADERS) {
-                    Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
-                    Log.d(TAG, "workspace layout: ");
-                    int nScreens = occupied.size();
-                    for (int y = 0; y < countY; y++) {
-                        String line = "";
-
-                        for (int i = 0; i < nScreens; i++) {
-                            long screenId = occupied.keyAt(i);
-                            if (screenId > 0) {
-                                line += " | ";
-                            }
-                        }
-                        Log.d(TAG, "[ " + line + " ]");
-                    }
-                }
             }
             if (LauncherAppState.PROFILE_STARTUP) {
                 Trace.endSection();
             }
         }
 
-        /**
-         * Partially updates the item without any notification. Must be called on the worker thread.
-         */
-        private void updateItem(long itemId, ContentValues update) {
-            mContext.getContentResolver().update(
-                    LauncherSettings.Favorites.CONTENT_URI,
-                    update,
-                    BaseColumns._ID + "= ?",
-                    new String[]{Long.toString(itemId)});
-        }
-
         /** Filters the set of items who are directly or indirectly (via another container) on the
          * specified screen. */
         private void filterCurrentWorkspaceItems(long currentScreenId,
@@ -1937,8 +1696,7 @@
         /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
          * right) */
         private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
-            final LauncherAppState app = LauncherAppState.getInstance();
-            final InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+            final InvariantDeviceProfile profile = mApp.getInvariantDeviceProfile();
             final int screenCols = profile.numColumns;
             final int screenCellCount = profile.numColumns * profile.numRows;
             Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
@@ -2257,7 +2015,7 @@
             for (UserHandle user : profiles) {
                 // Query for the set of apps
                 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-                final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
+                final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
                 if (DEBUG_LOADERS) {
                     Log.d(TAG, "getActivityList took "
                             + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
@@ -2271,9 +2029,9 @@
                 boolean quietMode = mUserManager.isQuietModeEnabled(user);
                 // Create the ApplicationInfos
                 for (int i = 0; i < apps.size(); i++) {
-                    LauncherActivityInfoCompat app = apps.get(i);
+                    LauncherActivityInfo app = apps.get(i);
                     // This builds the icon bitmaps.
-                    mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, quietMode));
+                    mBgAllAppsList.add(new AppInfo(mContext, app, user, quietMode), app);
                 }
 
                 final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
@@ -2465,17 +2223,16 @@
     }
 
     /**
-     * Repopulates the shortcut info, possibly updating any icon already on the workspace.
+     * Utility method to update a shortcut on the background thread.
      */
-    public void updateShortcutInfo(final ShortcutInfoCompat fullDetail, final ShortcutInfo info) {
+    public void updateAndBindShortcutInfo(final Provider<ShortcutInfo> shortcutProvider) {
         enqueueModelUpdateTask(new ExtendedModelTask() {
             @Override
             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
-                info.updateFromDeepShortcutInfo(fullDetail, app.getContext());
-
+                ShortcutInfo info = shortcutProvider.get();
                 ArrayList<ShortcutInfo> update = new ArrayList<>();
                 update.add(info);
-                bindUpdatedShortcuts(update, fullDetail.getUserHandle());
+                bindUpdatedShortcuts(update, info.user);
             }
         });
     }
@@ -2506,184 +2263,11 @@
                 bindWidgetsModel(callbacks);
 
                 // update the Widget entries inside DB on the worker thread.
-                LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(allWidgets);
+                mApp.getWidgetCache().removeObsoletePreviews(allWidgets);
             }
         });
     }
 
-    /**
-     * Make an ShortcutInfo object for a restored application or shortcut item that points
-     * to a package that is not yet installed on the system.
-     */
-    public ShortcutInfo getRestoredItemInfo(Cursor c, Intent intent,
-            int promiseType, int itemType, CursorIconInfo iconInfo) {
-        final ShortcutInfo info = new ShortcutInfo();
-        info.user = Process.myUserHandle();
-        info.promisedIntent = intent;
-
-        info.iconBitmap = iconInfo.loadIcon(c, info);
-        // the fallback icon
-        if (info.iconBitmap == null) {
-            mIconCache.getTitleAndIcon(info, false /* useLowResIcon */);
-        }
-
-        if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
-            String title = iconInfo.getTitle(c);
-            if (!TextUtils.isEmpty(title)) {
-                info.title = Utilities.trim(title);
-            }
-        } else if  ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
-            if (TextUtils.isEmpty(info.title)) {
-                info.title = iconInfo.getTitle(c);
-            }
-        } else {
-            throw new InvalidParameterException("Invalid restoreType " + promiseType);
-        }
-
-        info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
-        info.itemType = itemType;
-        info.status = promiseType;
-        return info;
-    }
-
-    /**
-     * Make an Intent object for a restored application or shortcut item that points
-     * to the market page for the item.
-     */
-    @Thunk Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
-        ComponentName componentName = intent.getComponent();
-        return getMarketIntent(componentName.getPackageName());
-    }
-
-    static Intent getMarketIntent(String packageName) {
-        return new Intent(Intent.ACTION_VIEW)
-            .setData(new Uri.Builder()
-                .scheme("market")
-                .authority("details")
-                .appendQueryParameter("id", packageName)
-                .build());
-    }
-
-    /**
-     * Make an ShortcutInfo object for a shortcut that is an application.
-     *
-     * If c is not null, then it will be used to fill in missing data like the title and icon.
-     */
-    public ShortcutInfo getAppShortcutInfo(Intent intent, UserHandle user, Cursor c,
-            CursorIconInfo iconInfo, boolean allowMissingTarget, boolean useLowResIcon) {
-        if (user == null) {
-            Log.d(TAG, "Null user found in getShortcutInfo");
-            return null;
-        }
-
-        ComponentName componentName = intent.getComponent();
-        if (componentName == null) {
-            Log.d(TAG, "Missing component found in getShortcutInfo");
-            return null;
-        }
-
-        Intent newIntent = new Intent(intent.getAction(), null);
-        newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-        newIntent.setComponent(componentName);
-        LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user);
-        if ((lai == null) && !allowMissingTarget) {
-            Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
-            return null;
-        }
-
-        final ShortcutInfo info = new ShortcutInfo();
-        info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
-        info.user = user;
-        info.intent = newIntent;
-
-        mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
-        if (mIconCache.isDefaultIcon(info.iconBitmap, user) && c != null) {
-            Bitmap icon = iconInfo.loadIcon(c);
-            info.iconBitmap = icon != null ? icon : mIconCache.getDefaultIcon(user);
-        }
-
-        if (lai != null && PackageManagerHelper.isAppSuspended(lai.getApplicationInfo())) {
-            info.isDisabled = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
-        }
-
-        // from the db
-        if (TextUtils.isEmpty(info.title) && c != null) {
-            info.title = iconInfo.getTitle(c);
-        }
-
-        // fall back to the class name of the activity
-        if (info.title == null) {
-            info.title = componentName.getClassName();
-        }
-
-        info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
-        return info;
-    }
-
-    /**
-     * Make an ShortcutInfo object for a shortcut that isn't an application.
-     */
-    @Thunk ShortcutInfo getShortcutInfo(Cursor c, CursorIconInfo iconInfo) {
-        final ShortcutInfo info = new ShortcutInfo();
-        // Non-app shortcuts are only supported for current user.
-        info.user = Process.myUserHandle();
-        info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
-
-        // TODO: If there's an explicit component and we can't install that, delete it.
-
-        loadInfoFromCursor(info, c, iconInfo);
-        return info;
-    }
-
-    /**
-     * Make an ShortcutInfo object for a shortcut that isn't an application.
-     */
-    public void loadInfoFromCursor(ShortcutInfo info, Cursor c, CursorIconInfo iconInfo) {
-        info.title = iconInfo.getTitle(c);
-        info.iconBitmap = iconInfo.loadIcon(c, info);
-        // the fallback icon
-        if (info.iconBitmap == null) {
-            info.iconBitmap = mIconCache.getDefaultIcon(info.user);
-        }
-    }
-
-    ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
-        Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
-        String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
-        Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
-
-        if (intent == null) {
-            // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
-            Log.e(TAG, "Can't construct ShorcutInfo with null intent");
-            return null;
-        }
-
-        final ShortcutInfo info = new ShortcutInfo();
-
-        // Only support intents for current user for now. Intents sent from other
-        // users wouldn't get here without intent forwarding anyway.
-        info.user = Process.myUserHandle();
-
-        if (bitmap instanceof Bitmap) {
-            info.iconBitmap = LauncherIcons.createIconBitmap((Bitmap) bitmap, context);
-        } else {
-            Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
-            if (extra instanceof ShortcutIconResource) {
-                info.iconResource = (ShortcutIconResource) extra;
-                info.iconBitmap = LauncherIcons.createIconBitmap(info.iconResource, context);
-            }
-        }
-        if (info.iconBitmap == null) {
-            info.iconBitmap = mIconCache.getDefaultIcon(info.user);
-        }
-
-        info.title = Utilities.trim(name);
-        info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
-        info.intent = intent;
-
-        return info;
-    }
-
     static boolean isValidProvider(AppWidgetProviderInfo provider) {
         return (provider != null) && (provider.provider != null)
                 && (provider.provider.getPackageName() != null);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 8f56eb6..6266fae 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -556,8 +556,7 @@
     }
 
     private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
-        int defaultLayout = LauncherAppState.getInstance()
-                .getInvariantDeviceProfile().defaultLayoutId;
+        int defaultLayout = LauncherAppState.getIDP(getContext()).defaultLayoutId;
         return new DefaultLayoutParser(getContext(), widgetHost,
                 mOpenHelper, getContext().getResources(), defaultLayout);
     }
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index 2976807..3256df6 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -17,7 +17,6 @@
 package com.android.launcher3;
 
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Resources.Theme;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -35,8 +34,11 @@
 import android.view.View.OnClickListener;
 
 import com.android.launcher3.graphics.DrawableFactory;
+import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.model.PackageItemInfo;
 
-public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implements OnClickListener {
+public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
+        implements OnClickListener, ItemInfoUpdateReceiver {
     private static final float SETUP_ICON_SIZE_FACTOR = 2f / 5;
     private static final float MIN_SATUNATION = 0.7f;
 
@@ -47,7 +49,6 @@
     private OnClickListener mClickListener;
     private final LauncherAppWidgetInfo mInfo;
     private final int mStartState;
-    private final Intent mIconLookupIntent;
     private final boolean mDisabledForSafeMode;
     private Launcher mLauncher;
 
@@ -68,7 +69,6 @@
         mLauncher = Launcher.getLauncher(context);
         mInfo = info;
         mStartState = info.restoreStatus;
-        mIconLookupIntent = new Intent().setComponent(info.providerName);
         mDisabledForSafeMode = disabledForSafeMode;
 
         mPaint = new TextPaint();
@@ -79,9 +79,13 @@
         setWillNotDraw(false);
 
         setElevation(getResources().getDimension(R.dimen.pending_widget_elevation));
-        updateIcon(cache);
         updateAppWidget(null);
         setOnClickListener(mLauncher);
+
+        // Load icon
+        PackageItemInfo item = new PackageItemInfo(info.providerName.getPackageName());
+        item.user = info.user;
+        cache.updateIconInBackground(this, item);
     }
 
     @Override
@@ -117,8 +121,9 @@
         mDrawableSizeChanged = true;
     }
 
-    private void updateIcon(IconCache cache) {
-        Bitmap icon = cache.getIcon(mIconLookupIntent, mInfo.user);
+    @Override
+    public void reapplyItemInfo(ItemInfoWithIcon info) {
+        Bitmap icon = info.iconBitmap;
         if (mIcon == icon) {
             return;
         }
@@ -157,6 +162,7 @@
             }
             mDrawableSizeChanged = true;
         }
+        invalidate();
     }
 
     private void updateSettingColor() {
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 05fb1ed..8c83dff 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -68,6 +68,7 @@
      * Indicates if it represents a common type mentioned in {@link CommonAppTypeParser}.
      * Upto 15 different types supported.
      */
+    @Deprecated
     public static final int FLAG_RESTORED_APP_TYPE = 0B0011110000;
 
     /**
@@ -234,7 +235,7 @@
         disabledMessage = shortcutInfo.getDisabledMessage();
 
         // TODO: Use cache for this
-        LauncherAppState launcherAppState = LauncherAppState.getInstance();
+        LauncherAppState launcherAppState = LauncherAppState.getInstance(context);
         Drawable unbadgedDrawable = DeepShortcutManager.getInstance(context)
                 .getShortcutIconDrawable(shortcutInfo,
                         launcherAppState.getInvariantDeviceProfile().fillResIconDpi);
@@ -248,11 +249,14 @@
 
     protected Bitmap getBadgedIcon(Bitmap unbadgedBitmap, ShortcutInfoCompat shortcutInfo,
             IconCache cache, Context context) {
-        unbadgedBitmap = LauncherIcons.addShadowToIcon(unbadgedBitmap);
+        unbadgedBitmap = LauncherIcons.addShadowToIcon(unbadgedBitmap, context);
         // Get the app info for the source activity.
         AppInfo appInfo = new AppInfo();
         appInfo.user = user;
         appInfo.componentName = shortcutInfo.getActivity();
+        appInfo.intent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_LAUNCHER)
+                .setComponent(shortcutInfo.getActivity());
         cache.getTitleAndIcon(appInfo, false);
         return LauncherIcons.badgeWithBitmap(unbadgedBitmap, appInfo.iconBitmap, context);
     }
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 73a9f64..e68a5b0 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -4,6 +4,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherActivityInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -11,7 +12,6 @@
 import android.util.AttributeSet;
 import android.widget.Toast;
 
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 
 public class UninstallDropTarget extends ButtonDropTarget {
@@ -69,7 +69,7 @@
             }
         }
         if (intent != null) {
-            LauncherActivityInfoCompat info = LauncherAppsCompat.getInstance(context)
+            LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
                     .resolveActivity(intent, user);
             if (info != null
                     && (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 2cb9138..89ffd31 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -104,6 +104,12 @@
     public static final boolean ATLEAST_LOLLIPOP_MR1 =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
 
+    /**
+     * Indicates if the device has a debug build. Should only be used to store additional info or
+     * add extra logging and not for changing the app behavior.
+     */
+    public static final boolean IS_DEBUG_DEVICE = Build.TYPE.toLowerCase().contains("debug");
+
     // An intent extra to indicate the horizontal scroll of the wallpaper.
     public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index cf6b025..1030779 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1613,8 +1613,7 @@
         Utilities.THREAD_POOL_EXECUTOR.execute(new Runnable() {
             @Override
             public void run() {
-                final Point size = LauncherAppState.getInstance()
-                        .getInvariantDeviceProfile().defaultWallpaperSize;
+                final Point size = LauncherAppState.getIDP(getContext()).defaultWallpaperSize;
                 if (size.x != mWallpaperManager.getDesiredMinimumWidth()
                         || size.y != mWallpaperManager.getDesiredMinimumHeight()) {
                     mWallpaperManager.suggestDesiredDimensions(size.x, size.y);
diff --git a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
index bc602f3..c71bc31 100644
--- a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
+++ b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
@@ -23,71 +23,73 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-
 import android.view.Gravity;
+
 import com.android.launcher3.R;
 
 /**
- * A helper class to positon and orient a drawable to be drawn.
- */
-class TransformedImageDrawable {
-    private Drawable mImage;
-    private float mXPercent;
-    private float mYPercent;
-    private int mGravity;
-    private int mAlpha;
-
-    /**
-     * @param gravity If one of the Gravity center values, the x and y offset will take the width
-     *                and height of the image into account to center the image to the offset.
-     */
-    public TransformedImageDrawable(Resources res, int resourceId, float xPct, float yPct,
-            int gravity) {
-        mImage = res.getDrawable(resourceId);
-        mXPercent = xPct;
-        mYPercent = yPct;
-        mGravity = gravity;
-    }
-
-    public void setAlpha(int alpha) {
-        mImage.setAlpha(alpha);
-        mAlpha = alpha;
-    }
-
-    public int getAlpha() {
-        return mAlpha;
-    }
-
-    public void updateBounds(Rect bounds) {
-        int width = mImage.getIntrinsicWidth();
-        int height = mImage.getIntrinsicHeight();
-        int left = bounds.left + (int) (mXPercent * bounds.width());
-        int top = bounds.top + (int) (mYPercent * bounds.height());
-        if ((mGravity & Gravity.CENTER_HORIZONTAL) == Gravity.CENTER_HORIZONTAL) {
-            left -= (width / 2);
-        }
-        if ((mGravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL) {
-            top -= (height / 2);
-        }
-        mImage.setBounds(left, top, left + width, top + height);
-    }
-
-    public void draw(Canvas canvas) {
-        int c = canvas.save(Canvas.MATRIX_SAVE_FLAG);
-        mImage.draw(canvas);
-        canvas.restoreToCount(c);
-    }
-}
-
-/**
  * This is a custom composite drawable that has a fixed virtual size and dynamically lays out its
  * children images relatively within its bounds.  This way, we can reduce the memory usage of a
  * single, large sparsely populated image.
  */
 public class AllAppsBackgroundDrawable extends Drawable {
 
-    private final TransformedImageDrawable mHand;
-    private final TransformedImageDrawable[] mIcons;
+    /**
+     * A helper class to positon and orient a drawable to be drawn.
+     */
+    protected static class TransformedImageDrawable {
+        private Drawable mImage;
+        private float mXPercent;
+        private float mYPercent;
+        private int mGravity;
+        private int mAlpha;
+
+        /**
+         * @param gravity If one of the Gravity center values, the x and y offset will take the width
+         *                and height of the image into account to center the image to the offset.
+         */
+        public TransformedImageDrawable(Resources res, int resourceId, float xPct, float yPct,
+                int gravity) {
+            mImage = res.getDrawable(resourceId);
+            mXPercent = xPct;
+            mYPercent = yPct;
+            mGravity = gravity;
+        }
+
+        public void setAlpha(int alpha) {
+            mImage.setAlpha(alpha);
+            mAlpha = alpha;
+        }
+
+        public int getAlpha() {
+            return mAlpha;
+        }
+
+        public void updateBounds(Rect bounds) {
+            int width = mImage.getIntrinsicWidth();
+            int height = mImage.getIntrinsicHeight();
+            int left = bounds.left + (int) (mXPercent * bounds.width());
+            int top = bounds.top + (int) (mYPercent * bounds.height());
+            if ((mGravity & Gravity.CENTER_HORIZONTAL) == Gravity.CENTER_HORIZONTAL) {
+                left -= (width / 2);
+            }
+            if ((mGravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL) {
+                top -= (height / 2);
+            }
+            mImage.setBounds(left, top, left + width, top + height);
+        }
+
+        public void draw(Canvas canvas) {
+            mImage.draw(canvas);
+        }
+
+        public Rect getBounds() {
+            return mImage.getBounds();
+        }
+    }
+
+    protected final TransformedImageDrawable mHand;
+    protected final TransformedImageDrawable[] mIcons;
     private final int mWidth;
     private final int mHeight;
 
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 10d4c15..a41d832 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -22,6 +22,7 @@
 import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.util.SparseIntArray;
+import android.view.MotionEvent;
 import android.view.View;
 
 import com.android.launcher3.BaseRecyclerView;
@@ -29,6 +30,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 import java.util.List;
@@ -207,7 +209,8 @@
 
         if (mApps.hasNoFilteredResults()) {
             if (mEmptySearchBackground == null) {
-                mEmptySearchBackground = new AllAppsBackgroundDrawable(getContext());
+                mEmptySearchBackground = DrawableFactory.get(getContext())
+                        .getAllAppsBackground(getContext());
                 mEmptySearchBackground.setAlpha(0);
                 mEmptySearchBackground.setCallback(this);
                 updateEmptySearchBackgroundBounds();
@@ -220,6 +223,16 @@
         }
     }
 
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent e) {
+        boolean result = super.onInterceptTouchEvent(e);
+        if (!result && e.getAction() == MotionEvent.ACTION_DOWN
+                && mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
+            mEmptySearchBackground.setHotspot(e.getX(), e.getY());
+        }
+        return result;
+    }
+
     /**
      * Maps the touch (from 0..1) to the adapter position that should be visible.
      */
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index c199fef..547ab2b 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -18,15 +18,12 @@
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.shortcuts.DeepShortcutsContainer;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.TouchController;
@@ -88,7 +85,6 @@
 
     private static final float RECATCH_REJECTION_FRACTION = .0875f;
 
-    private int mBezelSwipeUpHeight;
     private long mAnimationDuration;
 
     private AnimatorSet mCurrentAnimation;
@@ -104,8 +100,6 @@
         mDetector.setListener(this);
         mShiftRange = DEFAULT_SHIFT_RANGE;
         mProgress = 1f;
-        mBezelSwipeUpHeight = l.getResources().getDimensionPixelSize(
-                R.dimen.all_apps_bezel_swipe_height);
 
         mEvaluator = new ArgbEvaluator();
         mAllAppsBackgroundColor = ContextCompat.getColor(l, R.color.all_apps_container_color);
@@ -120,8 +114,6 @@
             } else if (mLauncher.isAllAppsVisible() &&
                     !mAppsView.shouldContainerScroll(ev)) {
                 mNoIntercept = true;
-            } else if (!mLauncher.isAllAppsVisible() && !shouldPossiblyIntercept(ev)) {
-                mNoIntercept = true;
             } else if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
                 mNoIntercept = true;
             } else {
@@ -150,6 +142,7 @@
                         ignoreSlopWhenSettling);
             }
         }
+
         if (mNoIntercept) {
             return false;
         }
@@ -160,25 +153,6 @@
         return mDetector.isDraggingOrSettling();
     }
 
-    private boolean shouldPossiblyIntercept(MotionEvent ev) {
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        if (mDetector.isIdleState()) {
-            if (grid.isVerticalBarLayout()) {
-                if (ev.getY() > mLauncher.getDeviceProfile().heightPx - mBezelSwipeUpHeight) {
-                    return true;
-                }
-            } else {
-                if (mLauncher.getDragLayer().isEventOverHotseat(ev) ||
-                        mLauncher.getDragLayer().isEventOverPageIndicator(ev)) {
-                    return true;
-                }
-            }
-            return false;
-        } else {
-            return true;
-        }
-    }
-
     @Override
     public boolean onControllerTouchEvent(MotionEvent ev) {
         return mDetector.onTouchEvent(ev);
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
deleted file mode 100644
index 830b60a..0000000
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.compat;
-
-import android.content.ComponentName;
-import android.content.pm.ApplicationInfo;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-
-public abstract class LauncherActivityInfoCompat {
-
-    LauncherActivityInfoCompat() {
-    }
-
-    public abstract ComponentName getComponentName();
-    public abstract UserHandle getUser();
-    public abstract CharSequence getLabel();
-    public abstract Drawable getIcon(int density);
-    public abstract ApplicationInfo getApplicationInfo();
-    public abstract long getFirstInstallTime();
-}
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
deleted file mode 100644
index c3b9592..0000000
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.compat;
-
-import android.content.ComponentName;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-
-public class LauncherActivityInfoCompatVL extends LauncherActivityInfoCompat {
-    private LauncherActivityInfo mLauncherActivityInfo;
-
-    LauncherActivityInfoCompatVL(LauncherActivityInfo launcherActivityInfo) {
-        super();
-        mLauncherActivityInfo = launcherActivityInfo;
-    }
-
-    public ComponentName getComponentName() {
-        return mLauncherActivityInfo.getComponentName();
-    }
-
-    public UserHandle getUser() {
-        return mLauncherActivityInfo.getUser();
-    }
-
-    public CharSequence getLabel() {
-        return mLauncherActivityInfo.getLabel();
-    }
-
-    public Drawable getIcon(int density) {
-        return mLauncherActivityInfo.getIcon(density);
-    }
-
-    public ApplicationInfo getApplicationInfo() {
-        return mLauncherActivityInfo.getApplicationInfo();
-    }
-
-    public long getFirstInstallTime() {
-        return mLauncherActivityInfo.getFirstInstallTime();
-    }
-}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 73ab36c..5c6eef8 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -56,9 +57,9 @@
         }
     }
 
-    public abstract List<LauncherActivityInfoCompat> getActivityList(String packageName,
+    public abstract List<LauncherActivityInfo> getActivityList(String packageName,
             UserHandle user);
-    public abstract LauncherActivityInfoCompat resolveActivity(Intent intent,
+    public abstract LauncherActivityInfo resolveActivity(Intent intent,
             UserHandle user);
     public abstract void startActivityForProfile(ComponentName component, UserHandle user,
             Rect sourceBounds, Bundle opts);
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index 60f9fab..e2739c1 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -29,7 +29,6 @@
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -45,26 +44,13 @@
     }
 
     @Override
-    public List<LauncherActivityInfoCompat> getActivityList(String packageName, UserHandle user) {
-        List<LauncherActivityInfo> list = mLauncherApps.getActivityList(packageName, user);
-        if (list.size() == 0) {
-            return Collections.emptyList();
-        }
-        ArrayList<LauncherActivityInfoCompat> compatList = new ArrayList<>(list.size());
-        for (LauncherActivityInfo info : list) {
-            compatList.add(new LauncherActivityInfoCompatVL(info));
-        }
-        return compatList;
+    public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
+        return mLauncherApps.getActivityList(packageName, user);
     }
 
     @Override
-    public LauncherActivityInfoCompat resolveActivity(Intent intent, UserHandle user) {
-        LauncherActivityInfo activity = mLauncherApps.resolveActivity(intent, user);
-        if (activity != null) {
-            return new LauncherActivityInfoCompatVL(activity);
-        } else {
-            return null;
-        }
+    public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
+        return mLauncherApps.resolveActivity(intent, user);
     }
 
     @Override
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 953d93d..b87582f 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -42,7 +42,7 @@
 
     PackageInstallerCompatVL(Context context) {
         mInstaller = context.getPackageManager().getPackageInstaller();
-        mCache = LauncherAppState.getInstance().getIconCache();
+        mCache = LauncherAppState.getInstance(context).getIconCache();
         mWorker = new Handler(LauncherModel.getWorkerLooper());
 
         mInstaller.registerSessionCallback(mCallback, mWorker);
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 4c12032..4a2a735 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -96,9 +96,7 @@
 
     public FolderPagedView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        LauncherAppState app = LauncherAppState.getInstance();
-
-        InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+        InvariantDeviceProfile profile = LauncherAppState.getIDP(context);
         mMaxCountX = profile.numFolderColumns;
         mMaxCountY = profile.numFolderRows;
 
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index f6a4b84..693df7a 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -23,6 +23,7 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AllAppsBackgroundDrawable;
 
 /**
  * Factory for creating new drawables.
@@ -50,4 +51,8 @@
         d.setFilterBitmap(true);
         return d;
     }
+
+    public AllAppsBackgroundDrawable getAllAppsBackground(Context context) {
+        return new AllAppsBackgroundDrawable(context);
+    }
 }
diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java
index 1410917..70b3dd6 100644
--- a/src/com/android/launcher3/graphics/IconNormalizer.java
+++ b/src/com/android/launcher3/graphics/IconNormalizer.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.graphics;
 
+import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -53,9 +54,9 @@
     private final float[] mLeftBorder;
     private final float[] mRightBorder;
 
-    private IconNormalizer() {
+    private IconNormalizer(Context context) {
         // Use twice the icon size as maximum size to avoid scaling down twice.
-        mMaxSize = LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize * 2;
+        mMaxSize = LauncherAppState.getIDP(context).iconBitmapSize * 2;
         mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8);
         mCanvas = new Canvas(mBitmap);
         mPixels = new byte[mMaxSize * mMaxSize];
@@ -239,10 +240,10 @@
         }
     }
 
-    public static IconNormalizer getInstance() {
+    public static IconNormalizer getInstance(Context context) {
         synchronized (LOCK) {
             if (sIconNormalizer == null) {
-                sIconNormalizer = new IconNormalizer();
+                sIconNormalizer = new IconNormalizer(context);
             }
         }
         return sIconNormalizer;
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 48f376f..8d8f3d9 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -20,9 +20,7 @@
 import android.content.Intent.ShortcutIconResource;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
-import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.PaintFlagsDrawFilter;
@@ -55,16 +53,6 @@
                 Paint.FILTER_BITMAP_FLAG));
     }
 
-
-    public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) {
-        byte[] data = c.getBlob(iconIndex);
-        try {
-            return createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length), context);
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
     /**
      * Returns a bitmap suitable for the all apps view. If the package or the resource do not
      * exist, it returns null.
@@ -76,9 +64,8 @@
             Resources resources = packageManager.getResourcesForApplication(iconRes.packageName);
             if (resources != null) {
                 final int id = resources.getIdentifier(iconRes.resourceName, null, null);
-                return createIconBitmap(
-                        resources.getDrawableForDensity(id, LauncherAppState.getInstance()
-                                .getInvariantDeviceProfile().fillResIconDpi), context);
+                return createIconBitmap(resources.getDrawableForDensity(
+                        id, LauncherAppState.getIDP(context).fillResIconDpi), context);
             }
         } catch (Exception e) {
             // Icon not found.
@@ -86,15 +73,11 @@
         return null;
     }
 
-    private static int getIconBitmapSize() {
-        return LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize;
-    }
-
     /**
      * Returns a bitmap which is of the appropriate size to be displayed as an icon
      */
     public static Bitmap createIconBitmap(Bitmap icon, Context context) {
-        final int iconBitmapSize = getIconBitmapSize();
+        final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
         if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) {
             return icon;
         }
@@ -108,7 +91,7 @@
     public static Bitmap createBadgedIconBitmap(
             Drawable icon, UserHandle user, Context context) {
         float scale = FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ?
-                1 : IconNormalizer.getInstance().getScale(icon, null);
+                1 : IconNormalizer.getInstance(context).getScale(icon, null);
         Bitmap bitmap = createIconBitmap(icon, context, scale);
         return badgeIconForUser(bitmap, user, context);
     }
@@ -138,7 +121,7 @@
     public static Bitmap createScaledBitmapWithoutShadow(Drawable icon, Context context) {
         RectF iconBounds = new RectF();
         float scale = FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ?
-                1 : IconNormalizer.getInstance().getScale(icon, iconBounds);
+                1 : IconNormalizer.getInstance(context).getScale(icon, iconBounds);
         scale = Math.min(scale, ShadowGenerator.getScaleForBounds(iconBounds));
         return createIconBitmap(icon, context, scale);
     }
@@ -147,8 +130,8 @@
      * Adds a shadow to the provided icon. It assumes that the icon has already been scaled using
      * {@link #createScaledBitmapWithoutShadow(Drawable, Context)}
      */
-    public static Bitmap addShadowToIcon(Bitmap icon) {
-        return ShadowGenerator.getInstance().recreateIcon(icon);
+    public static Bitmap addShadowToIcon(Bitmap icon, Context context) {
+        return ShadowGenerator.getInstance(context).recreateIcon(icon);
     }
 
     /**
@@ -180,7 +163,7 @@
     public static Bitmap createIconBitmap(Drawable icon, Context context, float scale) {
         icon = castToMaskableIconDrawable(icon);
         synchronized (sCanvas) {
-            final int iconBitmapSize = getIconBitmapSize();
+            final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
 
             int width = iconBitmapSize;
             int height = iconBitmapSize;
diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java
index 2b24ec9..8aea5a0 100644
--- a/src/com/android/launcher3/graphics/ShadowGenerator.java
+++ b/src/com/android/launcher3/graphics/ShadowGenerator.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.graphics;
 
+import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
 import android.graphics.BlurMaskFilter;
@@ -52,8 +53,8 @@
     private final Paint mBlurPaint;
     private final Paint mDrawPaint;
 
-    private ShadowGenerator() {
-        mIconSize = LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize;
+    private ShadowGenerator(Context context) {
+        mIconSize = LauncherAppState.getIDP(context).iconBitmapSize;
         mCanvas = new Canvas();
         mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
         mBlurPaint.setMaskFilter(new BlurMaskFilter(mIconSize * BLUR_FACTOR, Blur.NORMAL));
@@ -82,11 +83,11 @@
         return result;
     }
 
-    public static ShadowGenerator getInstance() {
+    public static ShadowGenerator getInstance(Context context) {
         Preconditions.assertNonUiThread();
         synchronized (LOCK) {
             if (sShadowGenerator == null) {
-                sShadowGenerator = new ShadowGenerator();
+                sShadowGenerator = new ShadowGenerator(context);
             }
         }
         return sShadowGenerator;
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 8629e92..ffb41b7 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -40,7 +40,7 @@
     private static File sLogsDirectory = null;
 
     public static void setDir(File logsDir) {
-        if (ProviderConfig.IS_DOGFOOD_BUILD) {
+        if (ProviderConfig.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE) {
             synchronized (DATE_FORMAT) {
                 // If the target directory changes, stop any active thread.
                 if (sHandler != null && !logsDir.equals(sLogsDirectory)) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 6dc5a36..2ac33ea 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -99,11 +99,11 @@
         deepShortcutMap.clear();
     }
 
-    public synchronized void removeItem(ItemInfo... items) {
-        removeItem(Arrays.asList(items));
+    public synchronized void removeItem(Context context, ItemInfo... items) {
+        removeItem(context, Arrays.asList(items));
     }
 
-    public synchronized void removeItem(Iterable<? extends ItemInfo> items) {
+    public synchronized void removeItem(Context context, Iterable<? extends ItemInfo> items) {
         for (ItemInfo item : items) {
             switch (item.itemType) {
                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
@@ -125,7 +125,6 @@
                     // Decrement pinned shortcut count
                     ShortcutKey pinnedShortcut = ShortcutKey.fromItemInfo(item);
                     MutableInt count = pinnedShortcutCounts.get(pinnedShortcut);
-                    Context context = LauncherAppState.getInstance().getContext();
                     if ((count == null || --count.value == 0)
                             && !InstallShortcutReceiver.getPendingShortcuts(context)
                                 .contains(pinnedShortcut)) {
@@ -146,7 +145,7 @@
         }
     }
 
-    public synchronized void addItem(ItemInfo item, boolean newItem) {
+    public synchronized void addItem(Context context, ItemInfo item, boolean newItem) {
         itemsIdMap.put(item.id, item);
         switch (item.itemType) {
             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
@@ -166,8 +165,7 @@
 
                 // Since this is a new item, pin the shortcut in the system server.
                 if (newItem && count.value == 1) {
-                    DeepShortcutManager.getInstance(LauncherAppState.getInstance().getContext())
-                            .pinShortcut(pinnedShortcut);
+                    DeepShortcutManager.getInstance(context).pinShortcut(pinnedShortcut);
                 }
                 // Fall through
             }
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 599dcd0..bbc7ae4 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -895,7 +895,7 @@
      */
     public static boolean migrateGridIfNeeded(Context context) {
         SharedPreferences prefs = Utilities.getPrefs(context);
-        InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+        InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
 
         String gridSizeString = getPointString(idp.numColumns, idp.numRows);
 
@@ -915,8 +915,7 @@
             if (srcHotseatCount != idp.numHotseatIcons) {
                 // Migrate hotseat.
 
-                dbChanged = new GridSizeMigrationTask(context,
-                        LauncherAppState.getInstance().getInvariantDeviceProfile(),
+                dbChanged = new GridSizeMigrationTask(context, LauncherAppState.getIDP(context),
                         validPackages, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
             }
 
@@ -978,9 +977,9 @@
      * @return a map with occupied hotseat position set to non-null value.
      */
     public static LongArrayMap<Object> removeBrokenHotseatItems(Context context) throws Exception {
-        GridSizeMigrationTask task = new GridSizeMigrationTask(context,
-                LauncherAppState.getInstance().getInvariantDeviceProfile(),
-                getValidPackages(context), Integer.MAX_VALUE, Integer.MAX_VALUE);
+        GridSizeMigrationTask task = new GridSizeMigrationTask(
+                context, LauncherAppState.getIDP(context), getValidPackages(context),
+                Integer.MAX_VALUE, Integer.MAX_VALUE);
 
         // Load all the valid entries
         ArrayList<DbEntry> items = task.loadHotseatEntries();
@@ -1038,8 +1037,7 @@
         }
 
         protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
-            return new GridSizeMigrationTask(mContext,
-                    LauncherAppState.getInstance().getInvariantDeviceProfile(),
+            return new GridSizeMigrationTask(mContext, LauncherAppState.getIDP(mContext),
                     mValidPackages, sourceSize, nextSize).migrateWorkspace();
         }
     }
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
new file mode 100644
index 0000000..43e1bd6
--- /dev/null
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2017 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.model;
+
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.Intent.ShortcutIconResource;
+import android.content.pm.LauncherActivityInfo;
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.UserHandle;
+import android.provider.BaseColumns;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.LongSparseArray;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.GridOccupancy;
+import com.android.launcher3.util.LongArrayMap;
+import com.android.launcher3.util.PackageManagerHelper;
+
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+
+/**
+ * Extension of {@link Cursor} with utility methods for workspace loading.
+ */
+public class LoaderCursor extends CursorWrapper {
+
+    private static final String TAG = "LoaderCursor";
+
+    public final LongSparseArray<UserHandle> allUsers = new LongSparseArray<>();
+
+    private final Context mContext;
+    private final UserManagerCompat mUserManager;
+    private final IconCache mIconCache;
+    private final InvariantDeviceProfile mIDP;
+
+    private final ArrayList<Long> itemsToRemove = new ArrayList<>();
+    private final ArrayList<Long> restoredRows = new ArrayList<>();
+    private final LongArrayMap<GridOccupancy> occupied = new LongArrayMap<>();
+
+    private final int iconPackageIndex;
+    private final int iconResourceIndex;
+    private final int iconIndex;
+    public final int titleIndex;
+
+    private final int idIndex;
+    private final int containerIndex;
+    private final int itemTypeIndex;
+    private final int screenIndex;
+    private final int cellXIndex;
+    private final int cellYIndex;
+    private final int profileIdIndex;
+
+    // Properties loaded per iteration
+    public long serialNumber;
+    public UserHandle user;
+    public long id;
+    public long container;
+    public int itemType;
+
+    public LoaderCursor(Cursor c, LauncherAppState app) {
+        super(c);
+        mContext = app.getContext();
+        mIconCache = app.getIconCache();
+        mIDP = app.getInvariantDeviceProfile();
+        mUserManager = UserManagerCompat.getInstance(mContext);
+
+        // Init column indices
+        iconIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
+        iconPackageIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
+        iconResourceIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
+        titleIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
+
+        idIndex = getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
+        containerIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
+        itemTypeIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
+        screenIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
+        cellXIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
+        cellYIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
+        profileIdIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID);
+    }
+
+    @Override
+    public boolean moveToNext() {
+        boolean result = super.moveToNext();
+        if (result) {
+            // Load common properties.
+            itemType = getInt(itemTypeIndex);
+            container = getInt(containerIndex);
+            id = getLong(idIndex);
+            serialNumber = getInt(profileIdIndex);
+            user = allUsers.get(serialNumber);
+        }
+        return result;
+    }
+
+    public ShortcutInfo loadSimpleShortcut() {
+        final ShortcutInfo info = new ShortcutInfo();
+        // Non-app shortcuts are only supported for current user.
+        info.user = user;
+        info.itemType = itemType;
+        info.title = getTitle();
+        info.iconBitmap = loadIcon(info);
+        // the fallback icon
+        if (info.iconBitmap == null) {
+            info.iconBitmap = mIconCache.getDefaultIcon(info.user);
+        }
+
+        // TODO: If there's an explicit component and we can't install that, delete it.
+
+        return info;
+    }
+
+    /**
+     * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
+     */
+    protected Bitmap loadIcon(ShortcutInfo info) {
+        Bitmap icon = null;
+        if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+            String packageName = getString(iconPackageIndex);
+            String resourceName = getString(iconResourceIndex);
+            if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
+                info.iconResource = new ShortcutIconResource();
+                info.iconResource.packageName = packageName;
+                info.iconResource.resourceName = resourceName;
+                icon = LauncherIcons.createIconBitmap(info.iconResource, mContext);
+            }
+        }
+        if (icon == null) {
+            // Failed to load from resource, try loading from DB.
+            byte[] data = getBlob(iconIndex);
+            try {
+                icon = LauncherIcons.createIconBitmap(
+                        BitmapFactory.decodeByteArray(data, 0, data.length), mContext);
+            } catch (Exception e) {
+                return null;
+            }
+        }
+        return icon;
+    }
+
+    /**
+     * Returns the title or empty string
+     */
+    private String getTitle() {
+        String title = getString(titleIndex);
+        return TextUtils.isEmpty(title) ? "" : Utilities.trim(title);
+    }
+
+
+    /**
+     * Make an ShortcutInfo object for a restored application or shortcut item that points
+     * to a package that is not yet installed on the system.
+     */
+    public ShortcutInfo getRestoredItemInfo(Intent intent, int promiseType) {
+        final ShortcutInfo info = new ShortcutInfo();
+        info.user = user;
+        info.promisedIntent = intent;
+
+        info.iconBitmap = loadIcon(info);
+        // the fallback icon
+        if (info.iconBitmap == null) {
+            mIconCache.getTitleAndIcon(info, false /* useLowResIcon */);
+        }
+
+        if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
+            String title = getTitle();
+            if (!TextUtils.isEmpty(title)) {
+                info.title = Utilities.trim(title);
+            }
+        } else if  ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
+            if (TextUtils.isEmpty(info.title)) {
+                info.title = getTitle();
+            }
+        } else {
+            throw new InvalidParameterException("Invalid restoreType " + promiseType);
+        }
+
+        info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
+        info.itemType = itemType;
+        info.status = promiseType;
+        return info;
+    }
+
+    /**
+     * Make an ShortcutInfo object for a shortcut that is an application.
+     */
+    public ShortcutInfo getAppShortcutInfo(
+            Intent intent, boolean allowMissingTarget, boolean useLowResIcon) {
+        if (user == null) {
+            Log.d(TAG, "Null user found in getShortcutInfo");
+            return null;
+        }
+
+        ComponentName componentName = intent.getComponent();
+        if (componentName == null) {
+            Log.d(TAG, "Missing component found in getShortcutInfo");
+            return null;
+        }
+
+        Intent newIntent = new Intent(Intent.ACTION_MAIN, null);
+        newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        newIntent.setComponent(componentName);
+        LauncherActivityInfo lai = LauncherAppsCompat.getInstance(mContext)
+                .resolveActivity(newIntent, user);
+        if ((lai == null) && !allowMissingTarget) {
+            Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
+            return null;
+        }
+
+        final ShortcutInfo info = new ShortcutInfo();
+        info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+        info.user = user;
+        info.intent = newIntent;
+
+        mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
+        if (mIconCache.isDefaultIcon(info.iconBitmap, user)) {
+            Bitmap icon = loadIcon(info);
+            info.iconBitmap = icon != null ? icon : info.iconBitmap;
+        }
+
+        if (lai != null && PackageManagerHelper.isAppSuspended(lai.getApplicationInfo())) {
+            info.isDisabled = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+        }
+
+        // from the db
+        if (TextUtils.isEmpty(info.title)) {
+            info.title = getTitle();
+        }
+
+        // fall back to the class name of the activity
+        if (info.title == null) {
+            info.title = componentName.getClassName();
+        }
+
+        info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
+        return info;
+    }
+
+    /**
+     * Returns a {@link ContentWriter} which can be used to update the current item.
+     */
+    public ContentWriter updater() {
+       return new ContentWriter(mContext, new ContentWriter.CommitParams(
+               BaseColumns._ID + "= ?", new String[]{Long.toString(id)}));
+    }
+
+    /**
+     * Marks the current item for removal
+     */
+    public void markDeleted(String reason) {
+        FileLog.e(TAG, reason);
+        itemsToRemove.add(id);
+    }
+
+    /**
+     * Removes any items marked for removal.
+     * @return true is any item was removed.
+     */
+    public boolean commitDeleted() {
+        if (itemsToRemove.size() > 0) {
+            // Remove dead items
+            mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI,
+                    Utilities.createDbSelectionQuery(
+                            LauncherSettings.Favorites._ID, itemsToRemove), null);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Marks the current item as restored
+     */
+    public void markRestored() {
+        restoredRows.add(id);
+    }
+
+    public void commitRestoredItems() {
+        if (restoredRows.size() > 0) {
+            // Update restored items that no longer require special handling
+            ContentValues values = new ContentValues();
+            values.put(LauncherSettings.Favorites.RESTORED, 0);
+            mContext.getContentResolver().update(LauncherSettings.Favorites.CONTENT_URI, values,
+                    Utilities.createDbSelectionQuery(
+                            LauncherSettings.Favorites._ID, restoredRows), null);
+        }
+    }
+
+    /**
+     * Returns true is the item is on workspace or hotseat
+     */
+    public boolean isOnWorkspaceOrHotseat() {
+        return container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
+                container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+    }
+
+    /**
+     * Applies the following properties:
+     * {@link ItemInfo#id}
+     * {@link ItemInfo#container}
+     * {@link ItemInfo#screenId}
+     * {@link ItemInfo#cellX}
+     * {@link ItemInfo#cellY}
+     */
+    public void applyCommonProperties(ItemInfo info) {
+        info.id = id;
+        info.container = container;
+        info.screenId = getInt(screenIndex);
+        info.cellX = getInt(cellXIndex);
+        info.cellY = getInt(cellYIndex);
+    }
+
+    /**
+     * Adds the {@param info} to {@param dataModel} if it does not overlap with any other item,
+     * otherwise marks it for deletion.
+     */
+    public void checkAndAddItem(ItemInfo info, BgDataModel dataModel) {
+        if (checkItemPlacement(info, dataModel.workspaceScreens)) {
+            dataModel.addItem(mContext, info, false);
+        } else {
+            markDeleted("Item position overlap");
+        }
+    }
+
+    /**
+     * check & update map of what's occupied; used to discard overlapping/invalid items
+     */
+    protected boolean checkItemPlacement(ItemInfo item, ArrayList<Long> workspaceScreens) {
+        long containerIndex = item.screenId;
+        if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+            // Return early if we detect that an item is under the hotseat button
+            if (!FeatureFlags.NO_ALL_APPS_ICON &&
+                    mIDP.isAllAppsButtonRank((int) item.screenId)) {
+                Log.e(TAG, "Error loading shortcut into hotseat " + item
+                        + " into position (" + item.screenId + ":" + item.cellX + ","
+                        + item.cellY + ") occupied by all apps");
+                return false;
+            }
+
+            final GridOccupancy hotseatOccupancy =
+                    occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
+
+            if (item.screenId >= mIDP.numHotseatIcons) {
+                Log.e(TAG, "Error loading shortcut " + item
+                        + " into hotseat position " + item.screenId
+                        + ", position out of bounds: (0 to " + (mIDP.numHotseatIcons - 1)
+                        + ")");
+                return false;
+            }
+
+            if (hotseatOccupancy != null) {
+                if (hotseatOccupancy.cells[(int) item.screenId][0]) {
+                    Log.e(TAG, "Error loading shortcut into hotseat " + item
+                            + " into position (" + item.screenId + ":" + item.cellX + ","
+                            + item.cellY + ") already occupied");
+                    return false;
+                } else {
+                    hotseatOccupancy.cells[(int) item.screenId][0] = true;
+                    return true;
+                }
+            } else {
+                final GridOccupancy occupancy = new GridOccupancy(mIDP.numHotseatIcons, 1);
+                occupancy.cells[(int) item.screenId][0] = true;
+                occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
+                return true;
+            }
+        } else if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+            if (!workspaceScreens.contains((Long) item.screenId)) {
+                // The item has an invalid screen id.
+                return false;
+            }
+        } else {
+            // Skip further checking if it is not the hotseat or workspace container
+            return true;
+        }
+
+        final int countX = mIDP.numColumns;
+        final int countY = mIDP.numRows;
+        if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
+                item.cellX < 0 || item.cellY < 0 ||
+                item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
+            Log.e(TAG, "Error loading shortcut " + item
+                    + " into cell (" + containerIndex + "-" + item.screenId + ":"
+                    + item.cellX + "," + item.cellY
+                    + ") out of screen bounds ( " + countX + "x" + countY + ")");
+            return false;
+        }
+
+        if (!occupied.containsKey(item.screenId)) {
+            GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1);
+            if (item.screenId == Workspace.FIRST_SCREEN_ID) {
+                // Mark the first row as occupied (if the feature is enabled)
+                // in order to account for the QSB.
+                screen.markCells(0, 0, countX + 1, 1, FeatureFlags.QSB_ON_FIRST_SCREEN);
+            }
+            occupied.put(item.screenId, screen);
+        }
+        final GridOccupancy occupancy = occupied.get(item.screenId);
+
+        // Check if any workspace icons overlap with each other
+        if (occupancy.isRegionVacant(item.cellX, item.cellY, item.spanX, item.spanY)) {
+            occupancy.markCells(item, true);
+            return true;
+        } else {
+            Log.e(TAG, "Error loading shortcut " + item
+                    + " into cell (" + containerIndex + "-" + item.screenId + ":"
+                    + item.cellX + "," + item.cellX + "," + item.spanX + "," + item.spanY
+                    + ") already occupied");
+            return false;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
index e05bf1e..baeaa94 100644
--- a/src/com/android/launcher3/model/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -28,7 +28,7 @@
      */
     public String packageName;
 
-    PackageItemInfo(String packageName) {
+    public PackageItemInfo(String packageName) {
         this.packageName = packageName;
     }
 
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
index 452dbe2..c256176 100644
--- a/src/com/android/launcher3/model/WidgetItem.java
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -31,16 +31,16 @@
     public final String label;
     public final int spanX, spanY;
 
-    public WidgetItem(LauncherAppWidgetProviderInfo info, PackageManager pm) {
+    public WidgetItem(LauncherAppWidgetProviderInfo info, PackageManager pm,
+            InvariantDeviceProfile idp) {
         super(info.provider, info.getProfile());
 
         label = Utilities.trim(info.getLabel(pm));
         widgetInfo = info;
         activityInfo = null;
 
-        InvariantDeviceProfile idv = LauncherAppState.getInstance().getInvariantDeviceProfile();
-        spanX = Math.min(info.spanX, idv.numColumns);
-        spanY = Math.min(info.spanY, idv.numRows);
+        spanX = Math.min(info.spanX, idp.numColumns);
+        spanY = Math.min(info.spanY, idp.numRows);
     }
 
     public WidgetItem(ResolveInfo info, PackageManager pm) {
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 2565985..2c62f89 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -60,12 +60,13 @@
         final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
         try {
             PackageManager pm = context.getPackageManager();
+            InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
 
             // Widgets
             for (AppWidgetProviderInfo widgetInfo :
                     AppWidgetManagerCompat.getInstance(context).getAllProviders()) {
-                widgetsAndShortcuts.add(new WidgetItem(
-                        LauncherAppWidgetProviderInfo.fromProviderInfo(context, widgetInfo), pm));
+                widgetsAndShortcuts.add(new WidgetItem(LauncherAppWidgetProviderInfo
+                        .fromProviderInfo(context, widgetInfo), pm, idp));
             }
 
             // Shortcuts
@@ -73,7 +74,7 @@
                     pm.queryIntentActivities(new Intent(Intent.ACTION_CREATE_SHORTCUT), 0)) {
                 widgetsAndShortcuts.add(new WidgetItem(info, pm));
             }
-            setWidgetsAndShortcuts(widgetsAndShortcuts);
+            setWidgetsAndShortcuts(widgetsAndShortcuts, context);
         } catch (Exception e) {
             if (!ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
                 // the returned value may be incomplete and will not be refreshed until the next
@@ -87,7 +88,8 @@
         return widgetsAndShortcuts;
     }
 
-    private void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts) {
+    private void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
+            Context context) {
         if (DEBUG) {
             Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
         }
@@ -99,7 +101,7 @@
         // clear the lists.
         mWidgetsList.clear();
 
-        InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+        InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
         UserHandle myUser = Process.myUserHandle();
 
         // add and update.
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index 9d8e62a..b0482f8 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -89,10 +89,12 @@
         ArrayList<Long> allScreens = LauncherDbUtils.getScreenIdsFromCursor(
                 mContext.getContentResolver().query(mOtherScreensUri, null, null, null,
                         LauncherSettings.WorkspaceScreens.SCREEN_RANK));
+        FileLog.d(TAG, "Importing DB from " + mOtherFavoritesUri);
 
         // During import we reset the screen IDs to 0-indexed values.
         if (allScreens.isEmpty()) {
             // No thing to migrate
+            FileLog.e(TAG, "No data found to import");
             return false;
         }
 
@@ -293,6 +295,7 @@
                 }
             }
         }
+        FileLog.d(TAG, totalItemsOnWorkspace + " items imported from external source");
         if (totalItemsOnWorkspace < MIN_ITEM_COUNT_FOR_SUCCESSFUL_MIGRATION) {
             throw new Exception("Insufficient data");
         }
@@ -303,7 +306,7 @@
         }
 
         LongArrayMap<Object> hotseatItems = GridSizeMigrationTask.removeBrokenHotseatItems(mContext);
-        int myHotseatCount = LauncherAppState.getInstance().getInvariantDeviceProfile().numHotseatIcons;
+        int myHotseatCount = LauncherAppState.getIDP(mContext).numHotseatIcons;
         if (!FeatureFlags.NO_ALL_APPS_ICON) {
             myHotseatCount--;
         }
@@ -378,8 +381,8 @@
         return c.getSharedPreferences(LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
     }
 
-    private static final int getMyHotseatLayoutId() {
-        return LauncherAppState.getInstance().getInvariantDeviceProfile().numHotseatIcons <= 5
+    private static final int getMyHotseatLayoutId(Context context) {
+        return LauncherAppState.getIDP(context).numHotseatIcons <= 5
                 ? R.xml.dw_phone_hotseat
                 : R.xml.dw_tablet_hotseat;
     }
@@ -389,7 +392,7 @@
      */
     private static class HotseatLayoutParser extends DefaultLayoutParser {
         public HotseatLayoutParser(Context context, LayoutParserCallback callback) {
-            super(context, null, callback, context.getResources(), getMyHotseatLayoutId());
+            super(context, null, callback, context.getResources(), getMyHotseatLayoutId(context));
         }
 
         @Override
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index 89fd99b..1758350 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -76,8 +76,7 @@
                 }
             }
 
-            new LossyScreenMigrationTask(
-                    context, LauncherAppState.getInstance().getInvariantDeviceProfile(), db)
+            new LossyScreenMigrationTask(context, LauncherAppState.getIDP(context), db)
                     .migrateScreen0();
             db.setTransactionSuccessful();
             return true;
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 47bee06..a200a85 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -142,6 +142,7 @@
     }
 
     public static void setPending(Context context, boolean isPending) {
+        FileLog.d(TAG, "Restore data received through full backup");
         Utilities.getPrefs(context).edit().putBoolean(RESTORE_TASK_PENDING, isPending).commit();
     }
 }
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index c83143b..38a3e1f 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -102,7 +102,7 @@
             }
 
             AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(activity);
-            InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+            InvariantDeviceProfile idp = LauncherAppState.getIDP(activity);
 
             Bundle opts = new Bundle();
             Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(activity, idp.numColumns, 1, null);
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 6796137..04c71ec 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.shortcuts.DeepShortcutsContainer.UnbadgedShortcutInfo;
 import com.android.launcher3.util.PillRevealOutlineProvider;
 import com.android.launcher3.util.PillWidthRevealOutlineProvider;
+import com.android.launcher3.util.Provider;
 
 /**
  * A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
@@ -114,10 +115,17 @@
      * Returns the shortcut info that is suitable to be added on the homescreen
      */
     public ShortcutInfo getFinalInfo() {
-        ShortcutInfo badged = new ShortcutInfo(mInfo);
+        final ShortcutInfo badged = new ShortcutInfo(mInfo);
         // Queue an update task on the worker thread. This ensures that the badged
         // shortcut eventually gets its icon updated.
-        Launcher.getLauncher(getContext()).getModel().updateShortcutInfo(mInfo.mDetail, badged);
+        Launcher.getLauncher(getContext()).getModel().updateAndBindShortcutInfo(
+                new Provider<ShortcutInfo>() {
+                    @Override
+                    public ShortcutInfo get() {
+                        badged.updateFromDeepShortcutInfo(mInfo.mDetail, getContext());
+                        return badged;
+                    }
+        });
         return badged;
     }
 
diff --git a/src/com/android/launcher3/testing/ToggleWeightWatcher.java b/src/com/android/launcher3/testing/ToggleWeightWatcher.java
index e08ec3a..f0c3920 100644
--- a/src/com/android/launcher3/testing/ToggleWeightWatcher.java
+++ b/src/com/android/launcher3/testing/ToggleWeightWatcher.java
@@ -22,7 +22,7 @@
         show = !show;
         sp.edit().putBoolean(TestingUtils.SHOW_WEIGHT_WATCHER, show).apply();
 
-        Launcher launcher = (Launcher) LauncherAppState.getInstance().getModel().getCallback();
+        Launcher launcher = (Launcher) LauncherAppState.getInstance(this).getModel().getCallback();
         if (launcher != null && launcher.mWeightWatcher != null) {
             launcher.mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE);
         }
diff --git a/src/com/android/launcher3/util/CachedPackageTracker.java b/src/com/android/launcher3/util/CachedPackageTracker.java
index ab5f44f..314b4c0 100644
--- a/src/com/android/launcher3/util/CachedPackageTracker.java
+++ b/src/com/android/launcher3/util/CachedPackageTracker.java
@@ -18,10 +18,10 @@
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.pm.LauncherActivityInfo;
 import android.os.UserHandle;
 
 import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.LauncherAppsCompat.OnAppsChangedCallbackCompat;
 import com.android.launcher3.compat.UserManagerCompat;
@@ -55,7 +55,7 @@
      * Checks the list of user apps, and generates package event accordingly.
      * {@see #onLauncherAppsAdded}, {@see #onLauncherPackageRemoved}
      */
-    public void processUserApps(List<LauncherActivityInfoCompat> apps, UserHandle user) {
+    public void processUserApps(List<LauncherActivityInfo> apps, UserHandle user) {
         String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
         HashSet<String> oldPackageSet = new HashSet<>();
         final boolean userAppsExisted = getUserApps(oldPackageSet, prefKey);
@@ -64,7 +64,7 @@
         HashSet<String> newPackageSet = new HashSet<>();
         ArrayList<LauncherActivityInstallInfo> packagesAdded = new ArrayList<>();
 
-        for (LauncherActivityInfoCompat info : apps) {
+        for (LauncherActivityInfo info : apps) {
             String packageName = info.getComponentName().getPackageName();
             newPackageSet.add(packageName);
             packagesRemoved.remove(packageName);
@@ -123,10 +123,10 @@
         HashSet<String> packageSet = new HashSet<>();
         final boolean userAppsExisted = getUserApps(packageSet, prefKey);
         if (!packageSet.contains(packageName)) {
-            List<LauncherActivityInfoCompat> activities =
+            List<LauncherActivityInfo> activities =
                     mLauncherApps.getActivityList(packageName, user);
             if (!activities.isEmpty()) {
-                LauncherActivityInfoCompat activityInfo = activities.get(0);
+                LauncherActivityInfo activityInfo = activities.get(0);
 
                 packageSet.add(packageName);
                 mPrefs.edit().putStringSet(prefKey, packageSet).apply();
@@ -172,10 +172,10 @@
 
     public static class LauncherActivityInstallInfo
             implements Comparable<LauncherActivityInstallInfo> {
-        public final LauncherActivityInfoCompat info;
+        public final LauncherActivityInfo info;
         public final long installTime;
 
-        public LauncherActivityInstallInfo(LauncherActivityInfoCompat info, long installTime) {
+        public LauncherActivityInstallInfo(LauncherActivityInfo info, long installTime) {
             this.info = info;
             this.installTime = installTime;
         }
diff --git a/src/com/android/launcher3/util/ContentWriter.java b/src/com/android/launcher3/util/ContentWriter.java
index 1c347c0..4384328 100644
--- a/src/com/android/launcher3/util/ContentWriter.java
+++ b/src/com/android/launcher3/util/ContentWriter.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.net.Uri;
 import android.os.UserHandle;
 
 import com.android.launcher3.LauncherAppState;
@@ -35,9 +36,15 @@
     private final ContentValues mValues;
     private final Context mContext;
 
+    private CommitParams mCommitParams;
     private Bitmap mIcon;
     private UserHandle mUser;
 
+    public ContentWriter(Context context, CommitParams commitParams) {
+        this(context);
+        mCommitParams = commitParams;
+    }
+
     public ContentWriter(Context context) {
         this(new ContentValues(), context);
     }
@@ -86,13 +93,34 @@
      * Commits any pending validation and returns the final values.
      * Must not be called on UI thread.
      */
-    public ContentValues getValues() {
+    public ContentValues getValues(Context context) {
         Preconditions.assertNonUiThread();
-        if (mIcon != null && !LauncherAppState.getInstance().getIconCache()
+        if (mIcon != null && !LauncherAppState.getInstance(context).getIconCache()
                 .isDefaultIcon(mIcon, mUser)) {
             mValues.put(LauncherSettings.Favorites.ICON, Utilities.flattenBitmap(mIcon));
             mIcon = null;
         }
         return mValues;
     }
+
+    public int commit() {
+        if (mCommitParams != null) {
+            return mContext.getContentResolver().update(mCommitParams.mUri, getValues(mContext),
+                    mCommitParams.mWhere, mCommitParams.mSelectionArgs);
+        }
+        return 0;
+    }
+
+    public static final class CommitParams {
+
+        final Uri mUri = LauncherSettings.Favorites.CONTENT_URI;
+        String mWhere;
+        String[] mSelectionArgs;
+
+        public CommitParams(String where, String[] selectionArgs) {
+            mWhere = where;
+            mSelectionArgs = selectionArgs;
+        }
+
+    }
 }
diff --git a/src/com/android/launcher3/util/CursorIconInfo.java b/src/com/android/launcher3/util/CursorIconInfo.java
deleted file mode 100644
index 3bc4eab..0000000
--- a/src/com/android/launcher3/util/CursorIconInfo.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.content.Context;
-import android.content.Intent.ShortcutIconResource;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.text.TextUtils;
-
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.graphics.LauncherIcons;
-
-/**
- * Utility class to load icon from a cursor.
- */
-public class CursorIconInfo {
-    public final int iconPackageIndex;
-    public final int iconResourceIndex;
-    public final int iconIndex;
-
-    public final int titleIndex;
-
-    private final Context mContext;
-
-    public CursorIconInfo(Context context, Cursor c) {
-        mContext = context;
-
-        iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
-        iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
-        iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
-
-        titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
-    }
-
-    /**
-     * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
-     */
-    public Bitmap loadIcon(Cursor c, ShortcutInfo info) {
-        Bitmap icon = null;
-        String packageName = c.getString(iconPackageIndex);
-        String resourceName = c.getString(iconResourceIndex);
-        if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
-            info.iconResource = new ShortcutIconResource();
-            info.iconResource.packageName = packageName;
-            info.iconResource.resourceName = resourceName;
-            icon = LauncherIcons.createIconBitmap(info.iconResource, mContext);
-        }
-        if (icon == null) {
-            // Failed to load from resource, try loading from DB.
-            icon = loadIcon(c);
-        }
-        return icon;
-    }
-
-    /**
-     * Loads the fixed bitmap from the icon if available.
-     */
-    public Bitmap loadIcon(Cursor c) {
-        return LauncherIcons.createIconBitmap(c, iconIndex, mContext);
-    }
-
-    /**
-     * Returns the title or empty string
-     */
-    public String getTitle(Cursor c) {
-        String title = c.getString(titleIndex);
-        return TextUtils.isEmpty(title) ? "" : Utilities.trim(c.getString(titleIndex));
-    }
-}
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index ce42bcd..af61554 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.pm.LauncherActivityInfo;
 import android.os.Process;
 import android.os.UserHandle;
 
@@ -31,7 +32,6 @@
 import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 
@@ -72,8 +72,8 @@
     private ManagedProfileHeuristic(Context context, UserHandle user) {
         mContext = context;
         mUser = user;
-        mModel = LauncherAppState.getInstance().getModel();
-        mIconCache = LauncherAppState.getInstance().getIconCache();
+        mModel = LauncherAppState.getInstance(context).getModel();
+        mIconCache = LauncherAppState.getInstance(context).getIconCache();
     }
 
     public void processPackageRemoved(String[] packages) {
@@ -92,7 +92,7 @@
         }
     }
 
-    public void processUserApps(List<LauncherActivityInfoCompat> apps) {
+    public void processUserApps(List<LauncherActivityInfo> apps) {
         Preconditions.assertWorkerThread();
         new ManagedProfilePackageHandler().processUserApps(apps, mUser);
     }
@@ -116,8 +116,9 @@
                     .isQuietModeEnabled(user);
             for (int i = 0; i < count; i++) {
                 LauncherActivityInstallInfo info = apps.get(i);
-                ShortcutInfo si = new AppInfo(mContext, info.info, user, mIconCache,
-                        quietModeEnabled, false /* useLowResIcon */).makeShortcut();
+                AppInfo appInfo = new AppInfo(mContext, info.info, user, quietModeEnabled);
+                mIconCache.getTitleAndIcon(appInfo, info.info, false /* useLowResIcon */);
+                ShortcutInfo si = appInfo.makeShortcut();
                 ((info.installTime <= folderCreationTime) ? workFolderApps : homescreenApps).add(si);
             }
 
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index b61d609..33a9fc6 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -23,6 +23,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.net.Uri;
 import android.os.Build;
 import android.text.TextUtils;
 
@@ -124,4 +125,13 @@
 
         return false;
     }
+
+    public static Intent getMarketIntent(String packageName) {
+        return new Intent(Intent.ACTION_VIEW)
+                .setData(new Uri.Builder()
+                        .scheme("market")
+                        .authority("details")
+                        .appendQueryParameter("id", packageName)
+                        .build());
+    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 310c1df..be4f635 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -87,7 +87,7 @@
         mLauncher = Launcher.getLauncher(context);
         mDragController = mLauncher.getDragController();
         mAdapter = new WidgetsListAdapter(this, this, context);
-        mIconCache = (LauncherAppState.getInstance()).getIconCache();
+        mIconCache = LauncherAppState.getInstance(context).getIconCache();
         if (LOGD) {
             Log.d(TAG, "WidgetsContainerView constructor");
         }
@@ -303,7 +303,7 @@
 
     private WidgetPreviewLoader getWidgetPreviewLoader() {
         if (mWidgetPreviewLoader == null) {
-            mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
+            mWidgetPreviewLoader = LauncherAppState.getInstance(getContext()).getWidgetCache();
         }
         return mWidgetPreviewLoader;
     }
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index f18313f..2d746d7 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -68,7 +68,7 @@
             View.OnLongClickListener iconLongClickListener,
             Context context) {
         mLayoutInflater = LayoutInflater.from(context);
-        mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
+        mWidgetPreviewLoader = LauncherAppState.getInstance(context).getWidgetCache();
 
         mIndexer = new AlphabeticIndexCompat(context);
 
diff --git a/src_config/com/android/launcher3/config/FeatureFlags.java b/src_config/com/android/launcher3/config/FeatureFlags.java
index 99d2654..4cad836 100644
--- a/src_config/com/android/launcher3/config/FeatureFlags.java
+++ b/src_config/com/android/launcher3/config/FeatureFlags.java
@@ -37,4 +37,6 @@
     public static final boolean PULLDOWN_SEARCH = false;
     // When enabled the status bar may show dark icons based on the top of the wallpaper.
     public static final boolean LIGHT_STATUS_BAR = false;
+    // When enabled allows to use any point on the fast scrollbar to start dragging.
+    public static final boolean LAUNCHER3_DIRECT_SCROLL = true;
 }
diff --git a/tests/Android.mk b/tests/Android.mk
index 466146e..5103ced 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -17,9 +17,9 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-#LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4
 
-#LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
 LOCAL_MIN_SDK_VERSION := 21
diff --git a/tests/res/raw/cache_data_updated_task_data.txt b/tests/res/raw/cache_data_updated_task_data.txt
index 9095476..8199687 100644
--- a/tests/res/raw/cache_data_updated_task_data.txt
+++ b/tests/res/raw/cache_data_updated_task_data.txt
@@ -22,7 +22,7 @@
 bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=9
 bgItem s itemType=1 status=1 title=app5-shrt intent=component=app5/class1 id=10
 
-allApps componentName=app1/class1
-allApps componentName=app1/class2
-allApps componentName=app2/class1
-allApps componentName=app2/class2
\ No newline at end of file
+allApps componentName=app1/class1 intent=component=app1/class1
+allApps componentName=app1/class2 intent=component=app1/class2
+allApps componentName=app2/class1 intent=component=app2/class1
+allApps componentName=app2/class2 intent=component=app2/class2
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/BindWidgetTest.java b/tests/src/com/android/launcher3/BindWidgetTest.java
index 72533e7..575b42b 100644
--- a/tests/src/com/android/launcher3/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/BindWidgetTest.java
@@ -16,7 +16,6 @@
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.ui.LauncherInstrumentationTestCase;
 import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.ManagedProfileHeuristic;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.WidgetHostViewLoader;
 
@@ -223,20 +222,9 @@
         item.screenId = screenId;
         item.onAddToDatabase(writer);
         writer.put(LauncherSettings.Favorites._ID, item.id);
-        mResolver.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues());
+        mResolver.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(mTargetContext));
+        resetLoaderState();
 
-        // Reset loader
-        try {
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext);
-                    LauncherAppState.getInstance().getModel().resetLoadedState(true, true);
-                }
-            });
-        } catch (Throwable t) {
-            throw new IllegalArgumentException(t);
-        }
         // Launch the home activity
         startLauncher();
         // Verify UI
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index b2f0cbb..2071389 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -164,7 +164,7 @@
                 info.cellX = x;
                 info.cellY = y;
                 info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
-                bgDataModel.addItem(info, false);
+                bgDataModel.addItem(targetContext, info, false);
             }
         }
         return startId;
diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index f23a574..9b7fc6e 100644
--- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -3,15 +3,18 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
 import android.os.Process;
 import android.os.UserHandle;
+import android.support.annotation.NonNull;
 import android.support.test.InstrumentationRegistry;
 import android.test.ProviderTestCase2;
 
 import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppFilter;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.DeferredHandler;
 import com.android.launcher3.IconCache;
@@ -21,9 +24,9 @@
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherModel.BaseModelUpdateTask;
 import com.android.launcher3.LauncherModel.Callbacks;
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.Provider;
 import com.android.launcher3.util.TestLauncherProvider;
 
 import org.mockito.ArgumentCaptor;
@@ -74,7 +77,7 @@
         idp = new InvariantDeviceProfile();
         iconCache = new MyIconCache(targetContext, idp);
 
-        allAppsList = new AllAppsList(iconCache, null);
+        allAppsList = new AllAppsList(iconCache, new AppFilter());
 
         when(appState.getIconCache()).thenReturn(iconCache);
         when(appState.getInvariantDeviceProfile()).thenReturn(idp);
@@ -125,11 +128,11 @@
                         classMap.put(commands[1], Class.forName(commands[2]));
                         break;
                     case "bgItem":
-                        bgDataModel.addItem(
+                        bgDataModel.addItem(targetContext,
                                 (ItemInfo) initItem(classMap.get(commands[1]), commands, 2), false);
                         break;
                     case "allApps":
-                        allAppsList.add((AppInfo) initItem(AppInfo.class, commands, 1));
+                        allAppsList.add((AppInfo) initItem(AppInfo.class, commands, 1), null);
                         break;
                 }
             }
@@ -184,9 +187,10 @@
         }
 
         @Override
-        protected CacheEntry cacheLocked(ComponentName componentName,
-                LauncherActivityInfoCompat info, UserHandle user,
-                boolean usePackageIcon, boolean useLowResIcon) {
+        protected CacheEntry cacheLocked(
+                @NonNull ComponentName componentName,
+                @NonNull Provider<LauncherActivityInfo> infoProvider,
+                UserHandle user, boolean usePackageIcon, boolean useLowResIcon) {
             CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
             if (entry == null) {
                 entry = new CacheEntry();
@@ -205,5 +209,10 @@
         public Bitmap newIcon() {
             return Bitmap.createBitmap(1, 1, Config.ARGB_8888);
         }
+
+        @Override
+        protected Bitmap makeDefaultIcon(UserHandle user) {
+            return newIcon();
+        }
     }
 }
diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index 646ef27..d595e6c 100644
--- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -30,6 +30,11 @@
     }
 
     public void testCacheUpdate_update_apps() throws Exception {
+        // Clear all icons from apps list so that its easy to check what was updated
+        for (AppInfo info : allAppsList.data) {
+            info.iconBitmap = null;
+        }
+
         executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1"));
 
         // Verify that only the app icons of app1 (id 1 & 2) are updated. Custom shortcut (id 7)
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
new file mode 100644
index 0000000..d40e6c2
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -0,0 +1,234 @@
+package com.android.launcher3.model;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.os.Process;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.LauncherAppsCompat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CELLX;
+import static com.android.launcher3.LauncherSettings.Favorites.CELLY;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.LauncherSettings.Favorites.ICON;
+import static com.android.launcher3.LauncherSettings.Favorites.ICON_PACKAGE;
+import static com.android.launcher3.LauncherSettings.Favorites.ICON_RESOURCE;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID;
+import static com.android.launcher3.LauncherSettings.Favorites.SCREEN;
+import static com.android.launcher3.LauncherSettings.Favorites.TITLE;
+import static com.android.launcher3.LauncherSettings.Favorites._ID;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link LoaderCursor}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LoaderCursorTest {
+
+    private LauncherAppState mMockApp;
+    private IconCache mMockIconCache;
+
+    private MatrixCursor mCursor;
+    private InvariantDeviceProfile mIDP;
+    private Context mContext;
+    private LauncherAppsCompat mLauncherApps;
+
+    private LoaderCursor mLoaderCursor;
+
+    @Before
+    public void setup() {
+        mIDP = new InvariantDeviceProfile();
+        mCursor = new MatrixCursor(new String[] {
+                ICON, ICON_PACKAGE, ICON_RESOURCE, TITLE,
+                _ID, CONTAINER, ITEM_TYPE, PROFILE_ID,
+                SCREEN, CELLX, CELLY,
+        });
+        mContext = InstrumentationRegistry.getTargetContext();
+
+        mMockApp = mock(LauncherAppState.class);
+        mMockIconCache = mock(IconCache.class);
+        when(mMockApp.getIconCache()).thenReturn(mMockIconCache);
+        when(mMockApp.getInvariantDeviceProfile()).thenReturn(mIDP);
+        when(mMockApp.getContext()).thenReturn(mContext);
+        mLauncherApps = LauncherAppsCompat.getInstance(mContext);
+
+        mLoaderCursor = new LoaderCursor(mCursor, mMockApp);
+        mLoaderCursor.allUsers.put(0, Process.myUserHandle());
+    }
+
+    private void initCursor(int itemType, String title) {
+        mCursor.newRow()
+                .add(_ID, 1)
+                .add(PROFILE_ID, 0)
+                .add(ITEM_TYPE, itemType)
+                .add(TITLE, title)
+                .add(CONTAINER, CONTAINER_DESKTOP);
+    }
+
+    @Test
+    public void getAppShortcutInfo_dontAllowMissing_invalidComponent() {
+        initCursor(ITEM_TYPE_APPLICATION, "");
+        assertTrue(mLoaderCursor.moveToNext());
+        ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
+        assertNull(mLoaderCursor.getAppShortcutInfo(
+                new Intent().setComponent(cn), false /* allowMissingTarget */, true));
+    }
+
+    @Test
+    public void getAppShortcutInfo_dontAllowMissing_validComponent() {
+        initCursor(ITEM_TYPE_APPLICATION, "");
+        assertTrue(mLoaderCursor.moveToNext());
+
+        ComponentName cn = mLauncherApps.getActivityList(null, mLoaderCursor.user)
+                .get(0).getComponentName();
+        ShortcutInfo info = mLoaderCursor.getAppShortcutInfo(
+                new Intent().setComponent(cn), false /* allowMissingTarget */, true);
+        assertNotNull(info);
+        assertTrue(Utilities.isLauncherAppTarget(info.intent));
+    }
+
+    @Test
+    public void getAppShortcutInfo_allowMissing_invalidComponent() {
+        initCursor(ITEM_TYPE_APPLICATION, "");
+        assertTrue(mLoaderCursor.moveToNext());
+
+        ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
+        ShortcutInfo info = mLoaderCursor.getAppShortcutInfo(
+                new Intent().setComponent(cn), true  /* allowMissingTarget */, true);
+        assertNotNull(info);
+        assertTrue(Utilities.isLauncherAppTarget(info.intent));
+    }
+
+    @Test
+    public void loadSimpleShortcut() {
+        initCursor(ITEM_TYPE_SHORTCUT, "my-shortcut");
+        assertTrue(mLoaderCursor.moveToNext());
+
+        Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+        when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user))).thenReturn(icon);
+        ShortcutInfo info = mLoaderCursor.loadSimpleShortcut();
+        assertEquals(icon, info.iconBitmap);
+        assertEquals("my-shortcut", info.title);
+        assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
+    }
+
+    @Test
+    public void checkItemPlacement_wrongWorkspaceScreen() {
+        ArrayList<Long> workspaceScreens = new ArrayList<>(Arrays.asList(1L, 3L));
+        mIDP.numRows = 4;
+        mIDP.numColumns = 4;
+        mIDP.numHotseatIcons = 3;
+
+        // Item on unknown screen are not placed
+        assertFalse(mLoaderCursor.checkItemPlacement(
+                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 4L), workspaceScreens));
+        assertFalse(mLoaderCursor.checkItemPlacement(
+                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 5L), workspaceScreens));
+        assertFalse(mLoaderCursor.checkItemPlacement(
+                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2L), workspaceScreens));
+
+        assertTrue(mLoaderCursor.checkItemPlacement(
+                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+        assertTrue(mLoaderCursor.checkItemPlacement(
+                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 3L), workspaceScreens));
+
+    }
+    @Test
+    public void checkItemPlacement_outsideBounds() {
+        ArrayList<Long> workspaceScreens = new ArrayList<>(Arrays.asList(1L, 2L));
+        mIDP.numRows = 4;
+        mIDP.numColumns = 4;
+        mIDP.numHotseatIcons = 3;
+
+        // Item outside screen bounds are not placed
+        assertFalse(mLoaderCursor.checkItemPlacement(
+                newItemInfo(4, 4, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+    }
+
+    @Test
+    public void checkItemPlacement_overlappingItems() {
+        ArrayList<Long> workspaceScreens = new ArrayList<>(Arrays.asList(1L, 2L));
+        mIDP.numRows = 4;
+        mIDP.numColumns = 4;
+        mIDP.numHotseatIcons = 3;
+
+        // Overlapping items are not placed
+        assertTrue(mLoaderCursor.checkItemPlacement(
+                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+        assertFalse(mLoaderCursor.checkItemPlacement(
+                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+
+        assertTrue(mLoaderCursor.checkItemPlacement(
+                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2L), workspaceScreens));
+        assertFalse(mLoaderCursor.checkItemPlacement(
+                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2L), workspaceScreens));
+
+        assertTrue(mLoaderCursor.checkItemPlacement(
+                newItemInfo(1, 1, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+        assertTrue(mLoaderCursor.checkItemPlacement(
+                newItemInfo(2, 2, 2, 2, CONTAINER_DESKTOP, 1L), workspaceScreens));
+
+        assertFalse(mLoaderCursor.checkItemPlacement(
+                newItemInfo(3, 2, 1, 2, CONTAINER_DESKTOP, 1L), workspaceScreens));
+    }
+
+    @Test
+    public void checkItemPlacement_hotseat() {
+        ArrayList<Long> workspaceScreens = new ArrayList<>();
+        mIDP.numRows = 4;
+        mIDP.numColumns = 4;
+        mIDP.numHotseatIcons = 3;
+
+        // Hotseat items are only placed based on screenId
+        assertTrue(mLoaderCursor.checkItemPlacement(
+                newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 1L), workspaceScreens));
+        assertTrue(mLoaderCursor.checkItemPlacement(
+                newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 2L), workspaceScreens));
+
+        assertFalse(mLoaderCursor.checkItemPlacement(
+                newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 3L), workspaceScreens));
+    }
+
+    private ItemInfo newItemInfo(int cellX, int cellY, int spanX, int spanY,
+            long container, long screenId) {
+        ItemInfo info = new ItemInfo();
+        info.cellX = cellX;
+        info.cellY = cellY;
+        info.spanX = spanX;
+        info.spanY = spanY;
+        info.container = container;
+        info.screenId = screenId;
+        return info;
+    }
+}
diff --git a/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java b/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java
index 4a80735..0ced7cf 100644
--- a/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java
+++ b/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java
@@ -1,12 +1,12 @@
 package com.android.launcher3.ui;
 
+import android.content.pm.LauncherActivityInfo;
 import android.os.Process;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.Until;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.util.Condition;
 import com.android.launcher3.util.Wait;
@@ -17,7 +17,7 @@
 @LargeTest
 public class AllAppsAppLaunchTest extends LauncherInstrumentationTestCase {
 
-    private LauncherActivityInfoCompat mSettingsApp;
+    private LauncherActivityInfo mSettingsApp;
 
     @Override
     protected void setUp() throws Exception {
diff --git a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
index 8837b00..3f77bfd 100644
--- a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
+++ b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
@@ -1,12 +1,12 @@
 package com.android.launcher3.ui;
 
+import android.content.pm.LauncherActivityInfo;
 import android.os.Process;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.Until;
 import android.test.suitebuilder.annotation.LargeTest;
 
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.util.Condition;
 import com.android.launcher3.util.Wait;
@@ -17,7 +17,7 @@
 @LargeTest
 public class AllAppsIconToHomeTest extends LauncherInstrumentationTestCase {
 
-    private LauncherActivityInfoCompat mSettingsApp;
+    private LauncherActivityInfo mSettingsApp;
 
     @Override
     protected void setUp() throws Exception {
diff --git a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
index fcf7122..42c6cd7 100644
--- a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
+++ b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
@@ -211,15 +211,22 @@
                 LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
         LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
                 LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
-        ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext);
+        resetLoaderState();
+    }
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                // Reset the loader state
-                LauncherAppState.getInstance().getModel().resetLoadedState(true, true);
-            }
-        });
+    protected void resetLoaderState() {
+        try {
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext);
+                    LauncherAppState.getInstance(mTargetContext).getModel()
+                            .resetLoadedState(true, true);
+                }
+            });
+        } catch (Throwable t) {
+            throw new IllegalArgumentException(t);
+        }
     }
 
     /**
@@ -248,8 +255,7 @@
         LauncherAppWidgetProviderInfo info = getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() {
             @Override
             public LauncherAppWidgetProviderInfo call() throws Exception {
-                InvariantDeviceProfile idv =
-                        LauncherAppState.getInstance().getInvariantDeviceProfile();
+                InvariantDeviceProfile idv = LauncherAppState.getIDP(mTargetContext);
 
                 ComponentName searchComponent = ((SearchManager) mTargetContext
                         .getSystemService(Context.SEARCH_SERVICE)).getGlobalSearchActivity();
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
index 02d938c..c6828f0 100644
--- a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
+++ b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
@@ -1,5 +1,6 @@
 package com.android.launcher3.ui;
 
+import android.content.pm.LauncherActivityInfo;
 import android.graphics.Point;
 import android.os.Process;
 import android.support.test.uiautomator.By;
@@ -9,7 +10,6 @@
 import android.view.MotionEvent;
 
 import com.android.launcher3.R;
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.util.Condition;
 import com.android.launcher3.util.Wait;
@@ -20,7 +20,7 @@
 @LargeTest
 public class ShortcutsLaunchTest extends LauncherInstrumentationTestCase {
 
-    private LauncherActivityInfoCompat mSettingsApp;
+    private LauncherActivityInfo mSettingsApp;
 
     @Override
     protected void setUp() throws Exception {
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
index afe4832..d573eea 100644
--- a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
+++ b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
@@ -1,5 +1,6 @@
 package com.android.launcher3.ui;
 
+import android.content.pm.LauncherActivityInfo;
 import android.graphics.Point;
 import android.os.Process;
 import android.support.test.uiautomator.By;
@@ -9,7 +10,6 @@
 import android.view.MotionEvent;
 
 import com.android.launcher3.R;
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.util.Condition;
 import com.android.launcher3.util.Wait;
@@ -20,7 +20,7 @@
 @LargeTest
 public class ShortcutsToHomeTest extends LauncherInstrumentationTestCase {
 
-    private LauncherActivityInfoCompat mSettingsApp;
+    private LauncherActivityInfo mSettingsApp;
 
     @Override
     protected void setUp() throws Exception {