Merge "Moving saving state transtion listener logic to individual views" into ub-launcher3-master
diff --git a/res/layout/appwidget_error.xml b/res/layout/appwidget_error.xml
index 708ece4..d6bd0c5 100644
--- a/res/layout/appwidget_error.xml
+++ b/res/layout/appwidget_error.xml
@@ -19,6 +19,7 @@
     android:layout_height="match_parent"
     android:gravity="center"
     android:elevation="2dp"
+    android:theme="@style/WidgetContainerTheme"
     android:background="@drawable/quantum_panel_dark"
     android:textAppearance="?android:attr/textAppearanceMediumInverse"
     android:textColor="@color/widgets_view_item_text_color"
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 3423835..32bccb8 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -27,6 +27,7 @@
             <enum name="all_apps" value="1" />
             <enum name="folder" value="2" />
             <enum name="widget_section" value="3" />
+            <enum name="shortcut_popup" value="4" />
         </attr>
         <attr name="deferShadowGeneration" format="boolean" />
         <attr name="customShadows" format="boolean" />
diff --git a/res/values/styles.xml b/res/values/styles.xml
index bb0bc2f..4e70f43 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -108,6 +108,7 @@
         <item name="android:shadowRadius">0</item>
         <item name="customShadows">false</item>
         <item name="layoutHorizontal">true</item>
+        <item name="iconDisplay">shortcut_popup</item>
         <item name="iconSizeOverride">@dimen/deep_shortcut_icon_size</item>
     </style>
 
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 979c950..84b504e 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -618,7 +618,9 @@
 
     public void setIsHotseat(boolean isHotseat) {
         mIsHotseat = isHotseat;
-        mShortcutsAndWidgets.setIsHotseat(isHotseat);
+        mShortcutsAndWidgets.setContainerType(isHotseat
+                ? ShortcutAndWidgetContainer.HOTSEAT
+                : ShortcutAndWidgetContainer.DEFAULT);
     }
 
     public boolean isHotseat() {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index cc21920..59ec56a 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -112,6 +112,7 @@
     public int hotseatIconSizePx;
     public int hotseatBarHeightPx;
     private int hotseatBarTopPaddingPx;
+    private int hotseatBarBottomPaddingPx;
     private int hotseatLandGutterPx;
 
     // All apps
@@ -185,6 +186,7 @@
         hotseatBarHeightPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height);
         hotseatBarTopPaddingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
+        hotseatBarBottomPaddingPx = 0;
         hotseatLandGutterPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_gutter_width);
 
         // Determine sizes.
@@ -207,7 +209,18 @@
         // In multi-window mode, we can have widthPx = availableWidthPx
         // and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
         // widthPx and heightPx values where it's needed.
-        return new DeviceProfile(context, inv, mwSize, mwSize, mwSize.x, mwSize.y, isLandscape);
+        DeviceProfile profile = new DeviceProfile(context, inv, mwSize, mwSize, mwSize.x, mwSize.y,
+                isLandscape);
+
+        // Hide labels on the workspace.
+        profile.iconTextSizePx = 0;
+        profile.cellHeightPx = profile.iconSizePx + profile.iconDrawablePaddingPx
+                + Utilities.calculateTextHeight(profile.iconTextSizePx);
+
+        // The nav bar is black so we add bottom padding to visually center hotseat icons.
+        profile.hotseatBarBottomPaddingPx = profile.hotseatBarTopPaddingPx;
+
+        return profile;
     }
 
     public void addLauncherLayoutChangedListener(LauncherLayoutChangeListener listener) {
@@ -396,7 +409,8 @@
                 availablePaddingX = (int) Math.min(availablePaddingX,
                             width * MAX_HORIZONTAL_PADDING_PERCENT);
                 int availablePaddingY = Math.max(0, height - topWorkspacePadding - paddingBottom
-                        - (int) (2 * inv.numRows * cellHeightPx));
+                        - (2 * inv.numRows * cellHeightPx) - hotseatBarTopPaddingPx
+                        - hotseatBarBottomPaddingPx);
                 padding.set(availablePaddingX / 2, topWorkspacePadding + availablePaddingY / 2,
                         availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
             } else {
@@ -529,7 +543,7 @@
             lp.height = hotseatBarHeightPx + mInsets.bottom;
             hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
                     hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
-                    mInsets.bottom);
+                    hotseatBarBottomPaddingPx + mInsets.bottom);
         } else {
             // For phones, layout the hotseat without any bottom margin
             // to ensure that we have space for the folders
@@ -538,7 +552,7 @@
             lp.height = hotseatBarHeightPx + mInsets.bottom;
             hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
                     hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
-                    mInsets.bottom);
+                    hotseatBarBottomPaddingPx + mInsets.bottom);
         }
         hotseat.setLayoutParams(lp);
 
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index d3e5350..657b024 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -20,10 +20,8 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
-import android.os.DeadObjectException;
-import android.os.TransactionTooLargeException;
+import android.util.SparseArray;
 import android.view.LayoutInflater;
-import android.view.View;
 
 import java.util.ArrayList;
 
@@ -36,6 +34,7 @@
 public class LauncherAppWidgetHost extends AppWidgetHost {
 
     private final ArrayList<Runnable> mProviderChangeListeners = new ArrayList<Runnable>();
+    private final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
 
     private Launcher mLauncher;
 
@@ -45,9 +44,11 @@
     }
 
     @Override
-    protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
+    protected LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId,
             AppWidgetProviderInfo appWidget) {
-        return new LauncherAppWidgetHostView(context);
+        LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
+        mViews.put(appWidgetId, view);
+        return view;
     }
 
     @Override
@@ -55,15 +56,13 @@
         try {
             super.startListening();
         } catch (Exception e) {
-            if (e.getCause() instanceof TransactionTooLargeException ||
-                    e.getCause() instanceof DeadObjectException) {
-                // We're willing to let this slide. The exception is being caused by the list of
-                // RemoteViews which is being passed back. The startListening relationship will
-                // have been established by this point, and we will end up populating the
-                // widgets upon bind anyway. See issue 14255011 for more context.
-            } else {
+            if (!Utilities.isBinderSizeError(e)) {
                 throw new RuntimeException(e);
             }
+            // We're willing to let this slide. The exception is being caused by the list of
+            // RemoteViews which is being passed back. The startListening relationship will
+            // have been established by this point, and we will end up populating the
+            // widgets upon bind anyway. See issue 14255011 for more context.
         }
     }
 
@@ -98,7 +97,24 @@
             lahv.updateLastInflationOrientation();
             return lahv;
         } else {
-            return super.createView(context, appWidgetId, appWidget);
+            try {
+                return super.createView(context, appWidgetId, appWidget);
+            } catch (Exception e) {
+                if (!Utilities.isBinderSizeError(e)) {
+                    throw new RuntimeException(e);
+                }
+
+                // If the exception was thrown while fetching the remote views, let the view stay.
+                // This will ensure that if the widget posts a valid update later, the view
+                // will update.
+                LauncherAppWidgetHostView view = mViews.get(appWidgetId);
+                if (view == null) {
+                    view = onCreateView(mLauncher, appWidgetId, appWidget);
+                }
+                view.setAppWidget(appWidgetId, appWidget);
+                view.switchToErrorView();
+                return  view;
+            }
         }
     }
 
@@ -114,4 +130,16 @@
         // launcher spans accordingly.
         info.initSpans();
     }
+
+    @Override
+    public void deleteAppWidgetId(int appWidgetId) {
+        super.deleteAppWidgetId(appWidgetId);
+        mViews.remove(appWidgetId);
+    }
+
+    @Override
+    protected void clearViews() {
+        super.clearViews();
+        mViews.clear();
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index b34d1ff..a4ea449 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -77,7 +77,7 @@
         mContext = context;
         mLongPressHelper = new CheckLongPressHelper(this);
         mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
-        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        mInflater = LayoutInflater.from(context);
         setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate());
         setBackgroundResource(R.drawable.widget_internal_focus_bg);
 
@@ -316,6 +316,11 @@
         setSelected(childIsFocused);
     }
 
+    public void switchToErrorView() {
+        // Update the widget with 0 Layout id, to reset the view to error view.
+        updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         try {
@@ -324,8 +329,7 @@
             post(new Runnable() {
                 @Override
                 public void run() {
-                    // Update the widget with 0 Layout id, to reset the view to error view.
-                    updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
+                    switchToErrorView();
                 }
             });
         }
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index c8bb793..dafc81a 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -18,13 +18,25 @@
 
 import android.app.WallpaperManager;
 import android.content.Context;
-import android.graphics.Paint;
 import android.graphics.Rect;
+import android.support.annotation.IntDef;
 import android.view.View;
 import android.view.ViewGroup;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 public class ShortcutAndWidgetContainer extends ViewGroup {
-    static final String TAG = "CellLayoutChildren";
+    static final String TAG = "ShortcutAndWidgetContainer";
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({DEFAULT, HOTSEAT, FOLDER})
+    public @interface ContainerType{}
+    public static final int DEFAULT = 0;
+    public static final int HOTSEAT = 1;
+    public static final int FOLDER = 2;
+
+    private int mContainerType = DEFAULT;
 
     // These are temporary variables to prevent having to allocate a new object just to
     // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
@@ -32,8 +44,6 @@
 
     private final WallpaperManager mWallpaperManager;
 
-    private boolean mIsHotseatLayout;
-
     private int mCellWidth;
     private int mCellHeight;
 
@@ -101,20 +111,19 @@
         mInvertIfRtl = invert;
     }
 
-    public void setIsHotseat(boolean isHotseat) {
-        mIsHotseatLayout = isHotseat;
-    }
-
-    int getCellContentWidth() {
-        final DeviceProfile grid = mLauncher.getDeviceProfile();
-        return Math.min(getMeasuredHeight(), mIsHotseatLayout ?
-                grid.hotseatCellWidthPx: grid.cellWidthPx);
+    public void setContainerType(@ContainerType int containerType) {
+        mContainerType = containerType;
     }
 
     int getCellContentHeight() {
         final DeviceProfile grid = mLauncher.getDeviceProfile();
-        return Math.min(getMeasuredHeight(), mIsHotseatLayout ?
-                grid.hotseatCellHeightPx : grid.cellHeightPx);
+        int cellContentHeight = grid.cellHeightPx;
+        if (mContainerType == HOTSEAT) {
+            cellContentHeight = grid.hotseatCellHeightPx;
+        } else if (mContainerType == FOLDER) {
+            cellContentHeight = grid.folderCellHeightPx;
+        }
+        return Math.min(getMeasuredHeight(), cellContentHeight);
     }
 
     public void measureChild(View child) {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index e82c32e..416ca8e 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -38,7 +38,9 @@
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.DeadObjectException;
 import android.os.PowerManager;
+import android.os.TransactionTooLargeException;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
@@ -648,4 +650,9 @@
             accessibilityManager.sendAccessibilityEvent(event);
         }
     }
+
+    public static boolean isBinderSizeError(Exception e) {
+        return e.getCause() instanceof TransactionTooLargeException
+                || e.getCause() instanceof DeadObjectException;
+    }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 0dd762f..01d87f4 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2521,6 +2521,7 @@
             onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d);
         } else if (mDragInfo != null) {
             final View cell = mDragInfo.cell;
+            boolean droppedOnOriginalCellDuringTransition = false;
 
             if (dropTargetLayout != null && !d.cancelled) {
                 // Move internally
@@ -2563,6 +2564,10 @@
                     minSpanY = item.minSpanY;
                 }
 
+                droppedOnOriginalCellDuringTransition = mIsSwitchingState
+                        && item.screenId == screenId && item.container == container
+                        && item.cellX == mTargetCell[0] && item.cellY == mTargetCell[1];
+
                 int[] resultSpan = new int[2];
                 mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
                         (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
@@ -2620,7 +2625,7 @@
                                 && !d.accessibleDrag) {
                             mDelayedResizeRunnable = new Runnable() {
                                 public void run() {
-                                    if (!isPageInTransition() && !mIsSwitchingState) {
+                                    if (!isPageInTransition()) {
                                         DragLayer dragLayer = mLauncher.getDragLayer();
                                         dragLayer.addResizeFrame(hostView, cellLayout);
                                     }
@@ -2655,6 +2660,17 @@
             };
             mAnimatingViewIntoPlace = true;
             if (d.dragView.hasDrawn()) {
+                if (droppedOnOriginalCellDuringTransition) {
+                    // Animate the item to its original position, while simultaneously exiting
+                    // spring-loaded mode so the page meets the icon where it was picked up.
+                    mLauncher.getDragController().animateDragViewToOriginalPosition(
+                            mDelayedResizeRunnable, cell,
+                            mStateTransitionAnimation.mSpringLoadedTransitionTime);
+                    mLauncher.exitSpringLoadedDragMode();
+                    mLauncher.getDropTargetBar().onDragEnd();
+                    parent.onDropChild(cell);
+                    return;
+                }
                 final ItemInfo info = (ItemInfo) cell.getTag();
                 boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
                         || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 176540d..5fc1d97 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -73,6 +73,7 @@
     private AllAppsSearchBarController mSearchBarController;
 
     private View mSearchContainer;
+    private int mSearchContainerMinHeight;
     private ExtendedEditText mSearchInput;
     private HeaderElevationController mElevationController;
 
@@ -98,6 +99,9 @@
         mApps.setAdapter(mAdapter);
         mLayoutManager = mAdapter.getLayoutManager();
         mSearchQueryBuilder = new SpannableStringBuilder();
+        mSearchContainerMinHeight
+                = getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_height);
+
         Selection.setSelection(mSearchQueryBuilder, 0);
     }
 
@@ -313,8 +317,9 @@
             if (!grid.isVerticalBarLayout()) {
                 MarginLayoutParams searchContainerLp =
                         (MarginLayoutParams) mSearchContainer.getLayoutParams();
+
                 searchContainerLp.height = mLauncher.getDragLayer().getInsets().top
-                        + grid.hotseatCellHeightPx;
+                        + mSearchContainerMinHeight;
                 mSearchContainer.setLayoutParams(searchContainerLp);
             }
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index e11bfc6..67ef5fc 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -295,10 +295,6 @@
         mIsInPreDrag = false;
     }
 
-    public boolean isInPreDrag() {
-        return mIsInPreDrag;
-    }
-
     /**
      * Call this from a drag source view like this:
      *
@@ -361,6 +357,8 @@
                 isDeferred = mDragObject.deferDragViewCleanupPostAnimation;
                 if (!isDeferred) {
                     mDragObject.dragView.remove();
+                } else if (mIsInPreDrag) {
+                    animateDragViewToOriginalPosition(null, null, -1);
                 }
                 mDragObject.dragView = null;
             }
@@ -374,6 +372,22 @@
         releaseVelocityTracker();
     }
 
+    public void animateDragViewToOriginalPosition(final Runnable onComplete,
+            final View originalIcon, int duration) {
+        Runnable onCompleteRunnable = new Runnable() {
+            @Override
+            public void run() {
+                if (originalIcon != null) {
+                    originalIcon.setVisibility(View.VISIBLE);
+                }
+                if (onComplete != null) {
+                    onComplete.run();
+                }
+            }
+        };
+        mDragObject.dragView.animateTo(mMotionDownX, mMotionDownY, onCompleteRunnable, duration);
+    }
+
     private void callOnDragEnd() {
         if (mIsInPreDrag && mOptions.preDragCondition != null) {
             mOptions.preDragCondition.onPreDragEnd(false /* dragStarted*/);
@@ -738,7 +752,7 @@
             if (dropTarget.acceptDrop(mDragObject)) {
                 if (flingVel != null) {
                     dropTarget.onFlingToDelete(mDragObject, flingVel);
-                } else {
+                } else if (!mIsInPreDrag) {
                     dropTarget.onDrop(mDragObject);
                 }
                 accepted = true;
@@ -749,11 +763,6 @@
         if (!mIsInPreDrag) {
             mDragObject.dragSource.onDropCompleted(
                     dropTargetAsView, mDragObject, flingVel != null, accepted);
-        } else {
-            // Only defer the drag view cleanup if the drag source handles the drop.
-            if (!(mDragObject.dragSource instanceof DropTarget)) {
-                mDragObject.deferDragViewCleanupPostAnimation = false;
-            }
         }
     }
 
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 8a2ae94..22e077b 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -23,7 +23,6 @@
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
-import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -38,11 +37,9 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.Thunk;
-
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.Thunk;
 
 import java.util.Arrays;
 
@@ -57,6 +54,8 @@
     @Thunk Paint mPaint;
     private final int mRegistrationX;
     private final int mRegistrationY;
+    private final float mInitialScale;
+    private final int[] mTempLoc = new int[2];
 
     private Point mDragVisualizeOffset = null;
     private Rect mDragRegion = null;
@@ -138,6 +137,8 @@
         mRegistrationX = registrationX;
         mRegistrationY = registrationY;
 
+        mInitialScale = initialScale;
+
         // Force a measure, because Workspace uses getMeasuredHeight() before the layout pass
         int ms = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
         measure(ms, ms);
@@ -356,6 +357,13 @@
         applyTranslation();
     }
 
+    public void animateTo(int toTouchX, int toTouchY, Runnable onCompleteRunnable, int duration) {
+        mTempLoc[0] = toTouchX - mRegistrationX;
+        mTempLoc[1] = toTouchY - mRegistrationY;
+        mDragLayer.animateViewIntoPosition(this, mTempLoc, 1f, mInitialScale, mInitialScale,
+                DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
+    }
+
     public void animateShift(final int shiftX, final int shiftY) {
         if (mAnim.isStarted()) {
             return;
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 2952196..53c12b5 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -1331,10 +1331,7 @@
             mIsExternalDrag = false;
         } else {
             currentDragView = mCurrentDragView;
-            // The view was never removed from this folder if we are still in the pre-drag.
-            if (!mDragController.isInPreDrag()) {
-                mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
-            }
+            mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
         }
 
         if (d.dragView.hasDrawn()) {
@@ -1355,12 +1352,9 @@
         mItemsInvalidated = true;
         rearrangeChildren();
 
-        // The ShortcutInfo was never removed if we are still in the pre-drag.
-        if (!mDragController.isInPreDrag()) {
-            // Temporarily suppress the listener, as we did all the work already here.
-            try (SuppressInfoChanges s = new SuppressInfoChanges()) {
-                mInfo.add(si, false);
-            }
+        // Temporarily suppress the listener, as we did all the work already here.
+        try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+            mInfo.add(si, false);
         }
 
         // Clear the drag info, as it is no longer being dragged.
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 31ec709..e71c5e9 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -256,6 +256,7 @@
         CellLayout page = new CellLayout(getContext());
         page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
         page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
+        page.getShortcutsAndWidgets().setContainerType(ShortcutAndWidgetContainer.FOLDER);
         page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
         page.setInvertIfRtl(true);
         page.setGridSize(mGridCountX, mGridCountY);
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 7286bf5..e3a2b24 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -87,6 +87,7 @@
         final int N = packages.length;
         FlagOp flagOp = FlagOp.NO_OP;
         final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages));
+        ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser);
         switch (mOp) {
             case OP_ADD: {
                 for (int i = 0; i < N; i++) {
@@ -135,15 +136,15 @@
                         FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED) :
                         FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED);
                 if (DEBUG) Log.d(TAG, "mAllAppsList.(un)suspend " + N);
-                appsList.updateDisabledFlags(
-                        ItemInfoMatcher.ofPackages(packageSet, mUser), flagOp);
+                appsList.updateDisabledFlags(matcher, flagOp);
                 break;
             case OP_USER_AVAILABILITY_CHANGE:
                 flagOp = UserManagerCompat.getInstance(context).isQuietModeEnabled(mUser)
                         ? FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER)
                         : FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER);
                 // We want to update all packages for this user.
-                appsList.updateDisabledFlags(ItemInfoMatcher.ofUser(mUser), flagOp);
+                matcher = ItemInfoMatcher.ofUser(mUser);
+                appsList.updateDisabledFlags(matcher, flagOp);
                 break;
         }
 
@@ -219,10 +220,10 @@
                         }
 
                         ComponentName cn = si.getTargetComponent();
-                        if (cn != null && packageSet.contains(cn.getPackageName())) {
+                        if (cn != null && matcher.matches(si, cn)) {
                             AppInfo appInfo = addedOrUpdatedApps.get(cn);
 
-                            if (si.isPromise()) {
+                            if (si.isPromise() && mOp == OP_ADD) {
                                 if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
                                     // Auto install icon
                                     PackageManager pm = context.getPackageManager();
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 3953f39..64043f4 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -6,17 +6,14 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.os.DeadObjectException;
-import android.os.TransactionTooLargeException;
 import android.util.Log;
 
 import com.android.launcher3.AppFilter;
 import com.android.launcher3.IconCache;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.compat.AlphabeticIndexCompat;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.config.ProviderConfig;
@@ -24,10 +21,7 @@
 import com.android.launcher3.util.Preconditions;
 
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
-import java.util.List;
 
 /**
  * Widgets data model that is used by the adapters of the widget views and controllers.
@@ -80,9 +74,7 @@
             }
             setWidgetsAndShortcuts(widgetsAndShortcuts);
         } catch (Exception e) {
-            if (!ProviderConfig.IS_DOGFOOD_BUILD &&
-                    (e.getCause() instanceof TransactionTooLargeException ||
-                            e.getCause() instanceof DeadObjectException)) {
+            if (!ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
                 // the returned value may be incomplete and will not be refreshed until the next
                 // time Launcher starts.
                 // TODO: after figuring out a repro step, introduce a dirty bit to check when
diff --git a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
index f592c57..fcf7122 100644
--- a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
+++ b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
@@ -196,7 +196,7 @@
         }
     }
 
-    private void sendPointer(int action, Point point) {
+    protected void sendPointer(int action, Point point) {
         MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
                 SystemClock.uptimeMillis(), action, point.x, point.y, 0);
         getInstrumentation().sendPointerSync(event);
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
new file mode 100644
index 0000000..f892e63
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
@@ -0,0 +1,68 @@
+package com.android.launcher3.ui;
+
+import android.graphics.Point;
+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 android.view.MotionEvent;
+
+import com.android.launcher3.R;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.Condition;
+import com.android.launcher3.util.Wait;
+
+/**
+ * Test for verifying that shortcuts are shown and can be launched after long pressing an app
+ */
+@LargeTest
+public class ShortcutsLaunchTest extends LauncherInstrumentationTestCase {
+
+    private LauncherActivityInfoCompat mSettingsApp;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
+                .getActivityList("com.android.settings", UserHandleCompat.myUserHandle()).get(0);
+    }
+
+    public void testAppLauncher_portrait() throws Exception {
+        lockRotation(true);
+        performTest();
+    }
+
+    public void testAppLauncher_landscape() throws Exception {
+        lockRotation(false);
+        performTest();
+    }
+
+    private void performTest() throws Exception {
+        startLauncher();
+
+        // Open all apps and wait for load complete
+        final UiObject2 appsContainer = openAllApps();
+        assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
+
+        // Find settings app and verify shortcuts appear when long pressed
+        UiObject2 icon = scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString()));
+        // Press icon center until shortcuts appear
+        Point iconCenter = icon.getVisibleCenter();
+        sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
+        UiObject2 deepShortcutsContainer = findViewById(R.id.deep_shortcuts_container);
+        assertNotNull(deepShortcutsContainer);
+        sendPointer(MotionEvent.ACTION_UP, iconCenter);
+
+        // Verify that launching a shortcut opens a page with the same text
+        assertTrue(deepShortcutsContainer.getChildCount() > 0);
+        UiObject2 shortcut = deepShortcutsContainer.getChildren().get(0)
+                .findObject(getSelectorForId(R.id.deep_shortcut));
+        shortcut.click();
+        assertTrue(mDevice.wait(Until.hasObject(By.pkg(
+                mSettingsApp.getComponentName().getPackageName())
+                .text(shortcut.getText())), DEFAULT_UI_TIMEOUT));
+    }
+}
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
new file mode 100644
index 0000000..f9a0e6e
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
@@ -0,0 +1,74 @@
+package com.android.launcher3.ui;
+
+import android.graphics.Point;
+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 android.view.MotionEvent;
+
+import com.android.launcher3.R;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.Condition;
+import com.android.launcher3.util.Wait;
+
+/**
+ * Test for dragging a deep shortcut to the home screen.
+ */
+@LargeTest
+public class ShortcutsToHomeTest extends LauncherInstrumentationTestCase {
+
+    private LauncherActivityInfoCompat mSettingsApp;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
+                .getActivityList("com.android.settings", UserHandleCompat.myUserHandle()).get(0);
+    }
+
+    public void testDragIcon_portrait() throws Throwable {
+        lockRotation(true);
+        performTest();
+    }
+
+    public void testDragIcon_landscape() throws Throwable {
+        lockRotation(false);
+        performTest();
+    }
+
+    private void performTest() throws Throwable {
+        clearHomescreen();
+        startLauncher();
+
+        // Open all apps and wait for load complete.
+        final UiObject2 appsContainer = openAllApps();
+        assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
+
+        // Find the app and long press it to show shortcuts.
+        UiObject2 icon = scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString()));
+        // Press icon center until shortcuts appear
+        Point iconCenter = icon.getVisibleCenter();
+        sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
+        UiObject2 deepShortcutsContainer = findViewById(R.id.deep_shortcuts_container);
+        assertNotNull(deepShortcutsContainer);
+        sendPointer(MotionEvent.ACTION_UP, iconCenter);
+
+        // Drag the first shortcut to the home screen.
+        assertTrue(deepShortcutsContainer.getChildCount() > 0);
+        UiObject2 shortcut = deepShortcutsContainer.getChildren().get(0)
+                .findObject(getSelectorForId(R.id.deep_shortcut));
+        String shortcutName = shortcut.getText();
+        dragToWorkspace(shortcut, false);
+
+        // Verify that the shortcut works on home screen
+        // (the app opens and has the same text as the shortcut).
+        mDevice.findObject(By.text(shortcutName)).click();
+        assertTrue(mDevice.wait(Until.hasObject(By.pkg(
+                mSettingsApp.getComponentName().getPackageName())
+                .text(shortcutName)), DEFAULT_UI_TIMEOUT));
+    }
+}