Merge "Moving the QSB out of the cell layout to the Drag layer" into ub-launcher3-calgary
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1ac6620..7bd9ff7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -112,6 +112,8 @@
     <string name="folder_hint_text">Unnamed Folder</string>
 
     <!-- Accessibility -->
+    <!-- The format string for when an app is temporarily disabled. -->
+    <string name="disabled_app_label">Disabled <xliff:g id="app_name" example="Messenger">%1$s</xliff:g></string>
     <skip />
 
     <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index ca60d5c..33e4e2a 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -153,34 +153,16 @@
 
     public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
             boolean promiseStateChanged) {
-        Bitmap b = info.getIcon(iconCache);
-
-        FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b);
-        if (info.isDisabled()) {
-            iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
-        }
-        setIcon(iconDrawable);
-        if (info.contentDescription != null) {
-            setContentDescription(info.contentDescription);
-        }
-        setText(info.title);
+        applyIconAndLabel(info.getIcon(iconCache), info);
         setTag(info);
-
         if (promiseStateChanged || info.isPromise()) {
             applyState(promiseStateChanged);
         }
     }
 
     public void applyFromApplicationInfo(AppInfo info) {
-        FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(info.iconBitmap);
-        if (info.isDisabled()) {
-            iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
-        }
-        setIcon(iconDrawable);
-        setText(info.title);
-        if (info.contentDescription != null) {
-            setContentDescription(info.contentDescription);
-        }
+        applyIconAndLabel(info.iconBitmap, info);
+
         // We don't need to check the info since it's not a ShortcutInfo
         super.setTag(info);
 
@@ -189,11 +171,7 @@
     }
 
     public void applyFromPackageItemInfo(PackageItemInfo info) {
-        setIcon(mLauncher.createIconDrawable(info.iconBitmap));
-        setText(info.title);
-        if (info.contentDescription != null) {
-            setContentDescription(info.contentDescription);
-        }
+        applyIconAndLabel(info.iconBitmap, info);
         // We don't need to check the info since it's not a ShortcutInfo
         super.setTag(info);
 
@@ -201,6 +179,20 @@
         verifyHighRes();
     }
 
+    private void applyIconAndLabel(Bitmap icon, ItemInfo info) {
+        FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(icon);
+        if (info.isDisabled()) {
+            iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
+        }
+        setIcon(iconDrawable);
+        setText(info.title);
+        if (info.contentDescription != null) {
+            setContentDescription(info.isDisabled()
+                    ? getContext().getString(R.string.disabled_app_label, info.contentDescription)
+                    : info.contentDescription);
+        }
+    }
+
     /**
      * Used for measurement only, sets some dummy values on this view.
      */
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 9604614..b7f033e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -116,6 +116,7 @@
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -1246,6 +1247,9 @@
                 // Close any open folders
                 closeFolder();
 
+                // Close any shortcuts containers
+                closeShortcutsContainer();
+
                 // Stop resizing any widgets
                 mWorkspace.exitWidgetResizeMode();
 
@@ -1847,6 +1851,7 @@
             mWorkspace.exitWidgetResizeMode();
 
             closeFolder(alreadyOnHome);
+            closeShortcutsContainer();
             exitSpringLoadedDragMode();
 
             // If we are already on home, then just animate back to the workspace,
@@ -1934,6 +1939,8 @@
         // TODO: Move folderInfo.isOpened out of the model and make it a UI state.
         closeFolder(false);
 
+        closeShortcutsContainer();
+
         if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 &&
                 mWaitingForResult) {
             ContentValues itemValues = new ContentValues();
@@ -2430,7 +2437,9 @@
             return;
         }
 
-        if (isAppsViewVisible()) {
+        if (getOpenShortcutsContainer() != null) {
+            closeShortcutsContainer();
+        } else if (isAppsViewVisible()) {
             showWorkspace(true);
         } else if (isWidgetsViewVisible())  {
             showOverviewMode(true);
@@ -3097,6 +3106,21 @@
         getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
     }
 
+    public void closeShortcutsContainer() {
+        DeepShortcutsContainer deepShortcutsContainer = getOpenShortcutsContainer();
+        if (deepShortcutsContainer != null) {
+            mDragController.removeDragListener(deepShortcutsContainer);
+            mDragLayer.removeView(deepShortcutsContainer);
+        }
+    }
+
+    /**
+     * @return The open shortcuts container, or null if there is none
+     */
+    public DeepShortcutsContainer getOpenShortcutsContainer() {
+        return (DeepShortcutsContainer) mDragLayer.findViewById(R.id.deep_shortcuts_container);
+    }
+
     @Override
     public boolean onLongClick(View v) {
         if (!isDraggingEnabled()) return false;
@@ -3358,6 +3382,7 @@
         mUserPresent = false;
         updateAutoAdvanceState();
         closeFolder();
+        closeShortcutsContainer();
 
         // Send an accessibility event to announce the context change
         getWindow().getDecorView()
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 39e28c0..a75edb7 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -71,10 +71,10 @@
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.StringFilter;
-import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.ViewOnDrawExecutor;
 
@@ -1942,7 +1942,16 @@
                                         List<ShortcutInfoCompat> fullDetails = mDeepShortcutManager
                                                 .queryForFullDetails(packageName,
                                                 Collections.singletonList(shortcutId), user);
-                                        if (fullDetails != null && !fullDetails.isEmpty()) {
+                                        if (fullDetails == null || fullDetails.isEmpty()) {
+                                            // There are no details for the shortcut. If this is due
+                                            // to a SecurityException, keep it in the database so
+                                            // we can restore the icon when the launcher regains
+                                            // permission. Otherwise remove the icon from the db.
+                                            if (!mDeepShortcutManager.wasLastCallSuccess()) {
+                                                itemsToRemove.add(id);
+                                                continue;
+                                            }
+                                        } else {
                                             pinnedShortcut = fullDetails.get(0);
                                             shouldPin = true;
                                         }
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 8b6e909..5f8faab 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -231,8 +231,7 @@
         }
 
         // Remove the shortcuts container when touching outside of it.
-        DeepShortcutsContainer deepShortcutsContainer = (DeepShortcutsContainer)
-                findViewById(R.id.deep_shortcuts_container);
+        DeepShortcutsContainer deepShortcutsContainer = mLauncher.getOpenShortcutsContainer();
         if (deepShortcutsContainer != null) {
             if (isEventOverView(deepShortcutsContainer, ev)) {
                 // Let the container handle the event.
@@ -244,7 +243,7 @@
                         return true;
                     }
                 } else {
-                    removeView(deepShortcutsContainer);
+                    mLauncher.closeShortcutsContainer();
                     // We let touches on the original icon go through so that users can launch
                     // the app with one tap if they don't find a shortcut they want.
                     return !isEventOverView(deepShortcutsContainer.getDeferredDragIcon(), ev);
@@ -547,10 +546,6 @@
         return new LayoutParams(p);
     }
 
-    public void setController(TouchController controller) {
-        mActiveController = controller;
-    }
-
     public static class LayoutParams extends InsettableFrameLayout.LayoutParams {
         public int x, y;
         public boolean customPosition = false;
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index 8bceda7..66e98cd 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -25,7 +25,6 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.launcher3.ItemInfo;
@@ -33,7 +32,6 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.UserHandleCompat;
 
-import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -53,14 +51,18 @@
             FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST;
 
     private final LauncherApps mLauncherApps;
+    private boolean mWasLastCallSuccess;
 
     public DeepShortcutManager(Context context, ShortcutCache shortcutCache) {
         mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
     }
 
     public static boolean supportsShortcuts(ItemInfo info) {
-        return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
-                || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+        return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+    }
+
+    public boolean wasLastCallSuccess() {
+        return mWasLastCallSuccess;
     }
 
     public void onShortcutsChanged(List<ShortcutInfoCompat> shortcuts) {
@@ -100,8 +102,10 @@
             pinnedIds.remove(id);
             try {
                 mLauncherApps.pinShortcuts(packageName, pinnedIds, user.getUser());
+                mWasLastCallSuccess = true;
             } catch (SecurityException e) {
-                Log.e(TAG, Log.getStackTraceString(e));
+                Log.w(TAG, "Failed to unpin shortcut", e);
+                mWasLastCallSuccess = false;
             }
         }
     }
@@ -120,8 +124,10 @@
             pinnedIds.add(id);
             try {
                 mLauncherApps.pinShortcuts(packageName, pinnedIds, user.getUser());
+                mWasLastCallSuccess = true;
             } catch (SecurityException e) {
-                Log.e(TAG, Log.getStackTraceString(e));
+                Log.w(TAG, "Failed to pin shortcut", e);
+                mWasLastCallSuccess = false;
             }
         }
     }
@@ -131,16 +137,12 @@
           Bundle startActivityOptions, UserHandleCompat user) {
         if (Utilities.isNycMR1OrAbove()) {
             try {
-                // TODO: remove reflection once updated SDK is ready.
-                // mLauncherApps.startShortcut(packageName, id, sourceBounds,
-                //        startActivityOptions, user.getUser());
-                mLauncherApps.getClass().getMethod("startShortcut", String.class, String.class,
-                        Rect.class, Bundle.class, UserHandle.class).invoke(mLauncherApps,
-                        packageName, id, sourceBounds, startActivityOptions, user.getUser());
+                mLauncherApps.startShortcut(packageName, id, sourceBounds,
+                        startActivityOptions, user.getUser());
+                mWasLastCallSuccess = true;
             } catch (SecurityException e) {
-                Log.e(TAG, Log.getStackTraceString(e));
-            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-                e.printStackTrace();
+                Log.e(TAG, "Failed to start shortcut", e);
+                mWasLastCallSuccess = false;
             }
         }
     }
@@ -149,10 +151,13 @@
     public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) {
         if (Utilities.isNycMR1OrAbove()) {
             try {
-                return mLauncherApps.getShortcutIconDrawable(shortcutInfo.getShortcutInfo(),
-                        density);
+                Drawable icon = mLauncherApps.getShortcutIconDrawable(
+                        shortcutInfo.getShortcutInfo(), density);
+                mWasLastCallSuccess = true;
+                return icon;
             } catch (SecurityException e) {
-                Log.e(TAG, Log.getStackTraceString(e));
+                Log.e(TAG, "Failed to get shortcut icon", e);
+                mWasLastCallSuccess = false;
             }
         }
         return null;
@@ -200,8 +205,10 @@
             List<ShortcutInfo> shortcutInfos = null;
             try {
                 shortcutInfos = mLauncherApps.getShortcuts(q, user.getUser());
+                mWasLastCallSuccess = true;
             } catch (SecurityException e) {
-                Log.e(TAG, Log.getStackTraceString(e));
+                Log.e(TAG, "Failed to query for shortcuts", e);
+                mWasLastCallSuccess = false;
             }
             if (shortcutInfos == null) {
                 return Collections.EMPTY_LIST;
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index 6f3875c..5479453 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -7,8 +7,9 @@
 import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.os.AsyncTask;
 import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
@@ -21,30 +22,30 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LogDecelerateInterpolator;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.TouchController;
 import com.android.launcher3.util.UiThreadCircularReveal;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
  * A container for shortcuts to deep links within apps.
  */
 @TargetApi(Build.VERSION_CODES.N)
-public class DeepShortcutsContainer extends LinearLayout implements  View.OnClickListener,
-        View.OnLongClickListener, View.OnTouchListener, DragSource,
-        UserEventDispatcher.LaunchSourceProvider, TouchController {
+public class DeepShortcutsContainer extends LinearLayout implements View.OnLongClickListener,
+        View.OnTouchListener, DragSource, DragController.DragListener,
+        UserEventDispatcher.LaunchSourceProvider {
     private static final String TAG = "ShortcutsContainer";
 
     private Launcher mLauncher;
@@ -98,37 +99,45 @@
         deferDrag(originalIcon);
 
         // Load the shortcuts on a background thread and update the container as it animates.
+        final Looper workerLooper = LauncherModel.getWorkerLooper();
+        final Handler uiHandler = new Handler(Looper.getMainLooper());
         final ItemInfo originalInfo = (ItemInfo) originalIcon.getTag();
         final UserHandleCompat user = originalInfo.user;
         final ComponentName activity = originalInfo.getTargetComponent();
-        new AsyncTask<Void, Void, List<ShortcutInfo>>() {
-            public List<ShortcutInfo> doInBackground(Void ... args) {
-                List<ShortcutInfoCompat> shortcuts = mDeepShortcutsManager
-                        .queryForAllAppShortcuts(activity, ids, user);
-                List<ShortcutInfo> shortcutInfos = new ArrayList<>(shortcuts.size());
-                for (ShortcutInfoCompat shortcut : shortcuts) {
-                    shortcutInfos.add(ShortcutInfo.fromDeepShortcutInfo(shortcut, mLauncher));
-                }
-                return shortcutInfos;
-            }
-
-            // TODO: implement onProgressUpdate() to load shortcuts one at a time.
-
+        new Handler(workerLooper).postAtFrontOfQueue(new Runnable() {
             @Override
-            protected void onPostExecute(List<ShortcutInfo> shortcuts) {
+            public void run() {
+                final List<ShortcutInfoCompat> shortcuts = mDeepShortcutsManager
+                        .queryForAllAppShortcuts(activity, ids, user);
                 for (int i = 0; i < shortcuts.size(); i++) {
-                    DeepShortcutView iconAndText = (DeepShortcutView) getChildAt(i);
-                    ShortcutInfo launcherShortcutInfo = shortcuts.get(i);
-                    iconAndText.applyFromShortcutInfo(launcherShortcutInfo,
-                            LauncherAppState.getInstance().getIconCache());
-                    iconAndText.setOnClickListener(DeepShortcutsContainer.this);
-                    iconAndText.setOnLongClickListener(DeepShortcutsContainer.this);
-                    iconAndText.setOnTouchListener(DeepShortcutsContainer.this);
-                    int viewId = mLauncher.getViewIdForItem(originalInfo);
-                    iconAndText.setId(viewId);
+                    final ShortcutInfoCompat shortcut = shortcuts.get(i);
+                    final ShortcutInfo launcherShortcutInfo = ShortcutInfo
+                            .fromDeepShortcutInfo(shortcut, mLauncher);
+                    uiHandler.post(new UpdateShortcutChild(i, launcherShortcutInfo));
                 }
             }
-        }.execute();
+        });
+    }
+
+    /** Updates the child of this container at the given index based on the given shortcut info. */
+    private class UpdateShortcutChild implements Runnable {
+        private int mShortcutChildIndex;
+        private ShortcutInfo mShortcutChildInfo;
+
+        public UpdateShortcutChild(int shortcutChildIndex, ShortcutInfo shortcutChildInfo) {
+            mShortcutChildIndex = shortcutChildIndex;
+            mShortcutChildInfo = shortcutChildInfo;
+        }
+
+        @Override
+        public void run() {
+            DeepShortcutView shortcutView = (DeepShortcutView) getChildAt(mShortcutChildIndex);
+            shortcutView.applyFromShortcutInfo(mShortcutChildInfo,
+                    LauncherAppState.getInstance().getIconCache());
+            shortcutView.setOnClickListener(mLauncher);
+            shortcutView.setOnLongClickListener(DeepShortcutsContainer.this);
+            shortcutView.setOnTouchListener(DeepShortcutsContainer.this);
+        }
     }
 
     // TODO: update this animation
@@ -194,6 +203,7 @@
     private void deferDrag(BubbleTextView originalIcon) {
         mDeferredDragIcon = originalIcon;
         showDragView(originalIcon);
+        mLauncher.getDragController().addDragListener(this);
     }
 
     public BubbleTextView getDeferredDragIcon() {
@@ -219,9 +229,9 @@
         mDragView.show(motionDownX, motionDownY);
     }
 
-    public boolean onForwardedEvent(MotionEvent ev, int activePointerId, MotionEvent touchDownEvent) {
-        mTouchDown = new Point((int) touchDownEvent.getX(), (int) touchDownEvent.getY());
+    public boolean onForwardedEvent(MotionEvent ev, int activePointerId, Point touchDown) {
         mActivePointerId = activePointerId;
+        mTouchDown = touchDown;
         return dispatchTouchEvent(ev);
     }
 
@@ -231,7 +241,6 @@
             return false;
         }
 
-
         final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
         if (activePointerIndex < 0) {
             return false;
@@ -256,12 +265,10 @@
 
             boolean containerContainsTouch = x >= 0 && y >= 0 && x < getWidth() && y < getHeight();
             if (shouldStartDeferredDrag((int) x, (int) y, containerContainsTouch)) {
-                mDeferredDragIcon.getParent().requestDisallowInterceptTouchEvent(false);
-                mDeferredDragIcon.setVisibility(VISIBLE);
-                mDeferredDragIcon.getOnLongClickListener().onLongClick(mDeferredDragIcon);
-                mLauncher.getDragLayer().removeView(this);
-                mLauncher.getDragController().onTouchEvent(ev);
                 cleanupDeferredDrag();
+                mDeferredDragIcon.getParent().requestDisallowInterceptTouchEvent(false);
+                mDeferredDragIcon.getOnLongClickListener().onLongClick(mDeferredDragIcon);
+                mLauncher.getDragController().onTouchEvent(ev);
                 return true;
             } else {
                 // Determine whether touch is over a shortcut.
@@ -288,7 +295,6 @@
                 }
             }
         } else if (action == MotionEvent.ACTION_UP) {
-            mDeferredDragIcon.setVisibility(VISIBLE);
             cleanupDeferredDrag();
             // Launch a shortcut if user was hovering over it.
             for (int i = 0; i < childCount; i++) {
@@ -316,9 +322,9 @@
      * @param y the y touch coordinate relative to this container
      */
     private boolean shouldStartDeferredDrag(int x, int y, boolean containerContainsTouch) {
-        Point closestEdge = new Point(mTouchDown.x, mIsAboveIcon ? getMeasuredHeight() : 0);
-        double distToEdge = Math.hypot(mTouchDown.x - closestEdge.x, mTouchDown.y - closestEdge.y);
-        double newDistToEdge = Math.hypot(x - closestEdge.x, y - closestEdge.y);
+        int closestEdgeY = mIsAboveIcon ? getMeasuredHeight() : 0;
+        double distToEdge = Math.abs(mTouchDown.y - closestEdgeY);
+        double newDistToEdge = Math.hypot(x - mTouchDown.x, y - closestEdgeY);
         return  !containerContainsTouch && (newDistToEdge - distToEdge > mStartDragThreshold);
     }
 
@@ -326,6 +332,7 @@
         if (mDragView != null) {
             mDragView.remove();
         }
+        mDeferredDragIcon.setVisibility(VISIBLE);
     }
 
     @Override
@@ -340,13 +347,6 @@
         return false;
     }
 
-    @Override
-    public void onClick(View view) {
-        // Clicked on a shortcut.
-        mLauncher.onClick(view);
-        ((DragLayer) getParent()).removeView(this);
-    }
-
     public boolean onLongClick(View v) {
         // Return early if this is not initiated from a touch
         if (!v.isInTouchMode()) return false;
@@ -354,11 +354,7 @@
         if (!mLauncher.isDraggingEnabled()) return false;
 
         // Long clicked on a shortcut.
-        // TODO remove this hack; it required because DragLayer isn't intercepting touch, so
-        // the controller is not updated from what it was previously.
-        mLauncher.getDragLayer().setController(mLauncher.getDragController());
         mLauncher.getWorkspace().beginDragShared(v, mIconLastTouchPos, this, false);
-        ((DragLayer) getParent()).removeView(this);
         // TODO: support dragging from within folder without having to close it
         mLauncher.closeFolder();
         return false;
@@ -401,6 +397,19 @@
     }
 
     @Override
+    public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
+        // Either the original icon or one of the shortcuts was dragged.
+        // Hide the container, but don't remove it yet because that interferes with touch events.
+        setVisibility(INVISIBLE);
+    }
+
+    @Override
+    public void onDragEnd() {
+        // Now remove the container.
+        mLauncher.closeShortcutsContainer();
+    }
+
+    @Override
     public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
         target.itemType = LauncherLogProto.SHORTCUT; // TODO: change to DYNAMIC_SHORTCUT
         target.gridX = info.cellX;
diff --git a/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java b/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java
index 956623e..63c8363 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java
@@ -1,6 +1,7 @@
 package com.android.launcher3.shortcuts;
 
 import android.content.Context;
+import android.graphics.Point;
 import android.os.SystemClock;
 import android.view.HapticFeedbackConstants;
 import android.view.LayoutInflater;
@@ -53,6 +54,8 @@
     private Launcher mLauncher;
     private DragLayer mDragLayer;
     private MotionEvent mTouchDownEvent;
+    /** The coordinates of the touch down, relative do the shortcuts container. */
+    private final Point mTouchDown;
 
     public ShortcutsContainerListener(BubbleTextView icon) {
         mSrcIcon = icon;
@@ -64,8 +67,8 @@
         icon.addOnAttachStateChangeListener(this);
 
         mLauncher = Launcher.getLauncher(mSrcIcon.getContext());
-
         mDragLayer = mLauncher.getDragLayer();
+        mTouchDown = new Point();
     }
 
     @Override
@@ -76,6 +79,9 @@
         }
 
         if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            if (mTouchDownEvent != null) {
+                mTouchDownEvent.recycle();
+            }
             mTouchDownEvent = MotionEvent.obtainNoHistory(event);
         }
 
@@ -134,6 +140,10 @@
             deepShortcutsContainer.populateAndShow(mSrcIcon, ids);
             mSrcIcon.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
                     HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+
+            // Convert touch down event to the container's coordinates.
+            Utilities.translateEventCoordinates(mSrcIcon, deepShortcutsContainer, mTouchDownEvent);
+            mTouchDown.set((int) mTouchDownEvent.getX(), (int) mTouchDownEvent.getY());
             return true;
         }
         return false;
@@ -243,8 +253,7 @@
     private boolean onTouchForwarded(MotionEvent srcEvent) {
         final View src = mSrcIcon;
 
-        final DeepShortcutsContainer dst = (DeepShortcutsContainer)
-                mDragLayer.findViewById(R.id.deep_shortcuts_container);
+        final DeepShortcutsContainer dst = mLauncher.getOpenShortcutsContainer();
         if (dst == null) {
             return false;
         }
@@ -253,16 +262,10 @@
         final MotionEvent dstEvent = MotionEvent.obtainNoHistory(srcEvent);
         Utilities.translateEventCoordinates(src, dst, dstEvent);
 
-        // Convert touch down event to destination-local coordinates.
-        // TODO: only create this once, or just store the x and y.
-        final MotionEvent touchDownEvent = MotionEvent.obtainNoHistory(mTouchDownEvent);
-        Utilities.translateEventCoordinates(src, dst, touchDownEvent);
-
         // Forward converted event to destination view, then recycle it.
         // TODO: don't create objects in onForwardedEvent.
-        final boolean handled = dst.onForwardedEvent(dstEvent, mActivePointerId, touchDownEvent);
+        final boolean handled = dst.onForwardedEvent(dstEvent, mActivePointerId, mTouchDown);
         dstEvent.recycle();
-        touchDownEvent.recycle();
 
         // Always cancel forwarding when the touch stream ends.
         final int action = srcEvent.getActionMasked();