OSDN Git Service

Merge "Slightly round corners of popup arrow" into ub-launcher3-dorval
[android-x86/packages-apps-Launcher3.git] / src / com / android / launcher3 / popup / PopupContainerWithArrow.java
index 7fda8b5..5891085 100644 (file)
@@ -19,21 +19,21 @@ package com.android.launcher3.popup;
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
-import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Color;
-import android.graphics.Point;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.drawable.ShapeDrawable;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
-import android.support.annotation.Nullable;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -48,30 +48,33 @@ import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
+import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherViewPropertyAnimator;
 import com.android.launcher3.LogAccelerateInterpolator;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
+import com.android.launcher3.anim.PropertyListBuilder;
+import com.android.launcher3.anim.PropertyResetListener;
 import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.graphics.TriangleShape;
 import com.android.launcher3.notification.NotificationItemView;
+import com.android.launcher3.notification.NotificationKeyData;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.shortcuts.ShortcutsItemView;
 import com.android.launcher3.util.PackageUserKey;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -83,18 +86,17 @@ import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
  * A container for shortcuts to deep links within apps.
  */
 @TargetApi(Build.VERSION_CODES.N)
-public class PopupContainerWithArrow extends AbstractFloatingView
-        implements View.OnLongClickListener, View.OnTouchListener, DragSource,
+public class PopupContainerWithArrow extends AbstractFloatingView implements DragSource,
         DragController.DragListener {
 
-    private final Point mIconShift = new Point();
-    private final Point mIconLastTouchPos = new Point();
-
     protected final Launcher mLauncher;
     private final int mStartDragThreshold;
     private LauncherAccessibilityDelegate mAccessibilityDelegate;
     private final boolean mIsRtl;
 
+    public ShortcutsItemView mShortcutsItemView;
+    private NotificationItemView mNotificationItemView;
+
     protected BubbleTextView mOriginalIcon;
     private final Rect mTempRect = new Rect();
     private PointF mInterceptTouchDown = new PointF();
@@ -140,35 +142,37 @@ public class PopupContainerWithArrow extends AbstractFloatingView
             return null;
         }
         ItemInfo itemInfo = (ItemInfo) icon.getTag();
+        if (!DeepShortcutManager.supportsShortcuts(itemInfo)) {
+            return null;
+        }
+
         List<String> shortcutIds = launcher.getPopupDataProvider().getShortcutIdsForItem(itemInfo);
-        String[] notificationKeys = launcher.getPopupDataProvider()
+        List<NotificationKeyData> notificationKeys = launcher.getPopupDataProvider()
                 .getNotificationKeysForItem(itemInfo);
-        if (shortcutIds.size() > 0 || notificationKeys.length > 0) {
-            final PopupContainerWithArrow container =
-                    (PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
-                            R.layout.popup_container, launcher.getDragLayer(), false);
-            container.setVisibility(View.INVISIBLE);
-            launcher.getDragLayer().addView(container);
-            container.populateAndShow(icon, shortcutIds, notificationKeys);
-            return container;
-        }
-        return null;
+
+        final PopupContainerWithArrow container =
+                (PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
+                        R.layout.popup_container, launcher.getDragLayer(), false);
+        container.setVisibility(View.INVISIBLE);
+        launcher.getDragLayer().addView(container);
+        container.populateAndShow(icon, shortcutIds, notificationKeys);
+        return container;
     }
 
     public void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds,
-            final String[] notificationKeys) {
+            final List<NotificationKeyData> notificationKeys) {
         final Resources resources = getResources();
-        final int arrowWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcuts_arrow_width);
-        final int arrowHeight = resources.getDimensionPixelSize(R.dimen.deep_shortcuts_arrow_height);
+        final int arrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
+        final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
         final int arrowHorizontalOffset = resources.getDimensionPixelSize(
-                R.dimen.deep_shortcuts_arrow_horizontal_offset);
+                R.dimen.popup_arrow_horizontal_offset);
         final int arrowVerticalOffset = resources.getDimensionPixelSize(
-                R.dimen.deep_shortcuts_arrow_vertical_offset);
+                R.dimen.popup_arrow_vertical_offset);
 
         // Add dummy views first, and populate with real info when ready.
         PopupPopulator.Item[] itemsToPopulate = PopupPopulator
                 .getItemsToPopulate(shortcutIds, notificationKeys);
-        addDummyViews(originalIcon, itemsToPopulate, notificationKeys.length > 1);
+        addDummyViews(originalIcon, itemsToPopulate, notificationKeys.size() > 1);
 
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
@@ -176,39 +180,32 @@ public class PopupContainerWithArrow extends AbstractFloatingView
         boolean reverseOrder = mIsAboveIcon;
         if (reverseOrder) {
             removeAllViews();
+            mNotificationItemView = null;
+            mShortcutsItemView = null;
             itemsToPopulate = PopupPopulator.reverseItems(itemsToPopulate);
-            addDummyViews(originalIcon, itemsToPopulate, notificationKeys.length > 1);
+            addDummyViews(originalIcon, itemsToPopulate, notificationKeys.size() > 1);
 
             measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
             orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
         }
 
-        List<DeepShortcutView> shortcutViews = new ArrayList<>();
-        NotificationItemView notificationView = null;
-        for (int i = 0; i < getChildCount(); i++) {
-            View item = getChildAt(i);
-            switch (itemsToPopulate[i]) {
-                case SHORTCUT:
-                    if (reverseOrder) {
-                        shortcutViews.add(0, (DeepShortcutView) item);
-                    } else {
-                        shortcutViews.add((DeepShortcutView) item);
-                    }
-                    break;
-                case NOTIFICATION:
-                    notificationView = (NotificationItemView) item;
-                    IconPalette iconPalette = originalIcon.getIconPalette();
-                    notificationView.applyColors(iconPalette);
-                    break;
-            }
+        ItemInfo originalItemInfo = (ItemInfo) originalIcon.getTag();
+        List<DeepShortcutView> shortcutViews = mShortcutsItemView == null
+                ? Collections.EMPTY_LIST
+                : mShortcutsItemView.getDeepShortcutViews(reverseOrder);
+        List<View> systemShortcutViews = mShortcutsItemView == null
+                ? Collections.EMPTY_LIST
+                : mShortcutsItemView.getSystemShortcutViews(reverseOrder);
+        if (mNotificationItemView != null) {
+            BadgeInfo badgeInfo = mLauncher.getPopupDataProvider()
+                    .getBadgeInfoForItem(originalItemInfo);
+            updateNotificationHeader(badgeInfo, originalIcon);
         }
 
         // Add the arrow.
         mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
         mArrow.setPivotX(arrowWidth / 2);
         mArrow.setPivotY(mIsAboveIcon ? 0 : arrowHeight);
-        PopupItemView firstItem = getItemViewAt(mIsAboveIcon ? getItemCount() - 1 : 0);
-        mArrow.setBackgroundTintList(firstItem.getAttachedArrowColor());
 
         animateOpen();
 
@@ -219,30 +216,51 @@ public class PopupContainerWithArrow extends AbstractFloatingView
         // Load the shortcuts on a background thread and update the container as it animates.
         final Looper workerLooper = LauncherModel.getWorkerLooper();
         new Handler(workerLooper).postAtFrontOfQueue(PopupPopulator.createUpdateRunnable(
-                mLauncher, (ItemInfo) originalIcon.getTag(), new Handler(Looper.getMainLooper()),
-                this, shortcutIds, shortcutViews, notificationKeys, notificationView));
+                mLauncher, originalItemInfo, new Handler(Looper.getMainLooper()),
+                this, shortcutIds, shortcutViews, notificationKeys, mNotificationItemView,
+                systemShortcutViews));
     }
 
     private void addDummyViews(BubbleTextView originalIcon,
-            PopupPopulator.Item[] itemsToPopulate, boolean secondaryNotificationViewHasIcons) {
+            PopupPopulator.Item[] itemTypesToPopulate, boolean notificationFooterHasIcons) {
         final Resources res = getResources();
-        final int spacing = res.getDimensionPixelSize(R.dimen.deep_shortcuts_spacing);
+        final int spacing = res.getDimensionPixelSize(R.dimen.popup_items_spacing);
         final LayoutInflater inflater = mLauncher.getLayoutInflater();
-        int numItems = itemsToPopulate.length;
+
+        int numItems = itemTypesToPopulate.length;
         for (int i = 0; i < numItems; i++) {
-            final PopupItemView item = (PopupItemView) inflater.inflate(
-                    itemsToPopulate[i].layoutId, this, false);
-            if (itemsToPopulate[i] == PopupPopulator.Item.NOTIFICATION) {
-                int secondaryHeight = secondaryNotificationViewHasIcons ?
-                        res.getDimensionPixelSize(R.dimen.notification_footer_height) :
-                        res.getDimensionPixelSize(R.dimen.notification_footer_collapsed_height);
-                item.findViewById(R.id.footer).getLayoutParams().height = secondaryHeight;
-            }
-            if (i < numItems - 1) {
-                ((LayoutParams) item.getLayoutParams()).bottomMargin = spacing;
+            PopupPopulator.Item itemTypeToPopulate = itemTypesToPopulate[i];
+            PopupPopulator.Item nextItemTypeToPopulate =
+                    i < numItems - 1 ? itemTypesToPopulate[i + 1] : null;
+            final View item = inflater.inflate(itemTypeToPopulate.layoutId, this, false);
+
+            if (itemTypeToPopulate == PopupPopulator.Item.NOTIFICATION) {
+                mNotificationItemView = (NotificationItemView) item;
+                int footerHeight = notificationFooterHasIcons ?
+                        res.getDimensionPixelSize(R.dimen.notification_footer_height) : 0;
+                item.findViewById(R.id.footer).getLayoutParams().height = footerHeight;
             }
+
+            boolean shouldAddBottomMargin = nextItemTypeToPopulate != null
+                    && itemTypeToPopulate.isShortcut ^ nextItemTypeToPopulate.isShortcut;
+
             item.setAccessibilityDelegate(mAccessibilityDelegate);
-            addView(item);
+            if (itemTypeToPopulate.isShortcut) {
+                if (mShortcutsItemView == null) {
+                    mShortcutsItemView = (ShortcutsItemView) inflater.inflate(
+                            R.layout.shortcuts_item, this, false);
+                    addView(mShortcutsItemView);
+                }
+                mShortcutsItemView.addShortcutView(item, itemTypeToPopulate);
+                if (shouldAddBottomMargin) {
+                    ((LayoutParams) mShortcutsItemView.getLayoutParams()).bottomMargin = spacing;
+                }
+            } else {
+                addView(item);
+                if (shouldAddBottomMargin) {
+                    ((LayoutParams) item.getLayoutParams()).bottomMargin = spacing;
+                }
+            }
         }
         // TODO: update this, since not all items are shortcuts
         setContentDescription(getContext().getString(R.string.shortcuts_menu_description,
@@ -298,7 +316,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView
             anim.setInterpolator(interpolator);
             shortcutAnims.play(anim);
 
-            Animator fadeAnim = new LauncherViewPropertyAnimator(popupItemView).alpha(1);
+            Animator fadeAnim = ObjectAnimator.ofFloat(popupItemView, View.ALPHA, 1);
             fadeAnim.setInterpolator(fadeInterpolator);
             // We want the shortcut to be fully opaque before the arrow starts animating.
             fadeAnim.setDuration(arrowScaleDelay);
@@ -318,9 +336,8 @@ public class PopupContainerWithArrow extends AbstractFloatingView
         // Animate the arrow
         mArrow.setScaleX(0);
         mArrow.setScaleY(0);
-        Animator arrowScale = new LauncherViewPropertyAnimator(mArrow).scaleX(1).scaleY(1);
+        Animator arrowScale = createArrowScaleAnim(1).setDuration(arrowScaleDuration);
         arrowScale.setStartDelay(arrowScaleDelay);
-        arrowScale.setDuration(arrowScaleDuration);
         shortcutAnims.play(arrowScale);
 
         mOpenCloseAnimator = shortcutAnims;
@@ -371,14 +388,14 @@ public class PopupContainerWithArrow extends AbstractFloatingView
             // Aligning with the shortcut icon.
             int shortcutIconWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcut_icon_size);
             int shortcutPaddingStart = resources.getDimensionPixelSize(
-                    R.dimen.deep_shortcut_padding_start);
+                    R.dimen.popup_padding_start);
             xOffset = iconWidth / 2 - shortcutIconWidth / 2 - shortcutPaddingStart;
         } else {
             // Aligning with the drag handle.
             int shortcutDragHandleWidth = resources.getDimensionPixelSize(
                     R.dimen.deep_shortcut_drag_handle_size);
             int shortcutPaddingEnd = resources.getDimensionPixelSize(
-                    R.dimen.deep_shortcut_padding_end);
+                    R.dimen.popup_padding_end);
             xOffset = iconWidth / 2 - shortcutDragHandleWidth / 2 - shortcutPaddingEnd;
         }
         x += mIsLeftAligned ? xOffset : -xOffset;
@@ -471,7 +488,11 @@ public class PopupContainerWithArrow extends AbstractFloatingView
         } else {
             ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
                     width, height, !mIsAboveIcon));
-            arrowDrawable.getPaint().setColor(Color.WHITE);
+            Paint arrowPaint = arrowDrawable.getPaint();
+            arrowPaint.setColor(Color.WHITE);
+            // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
+            int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
+            arrowPaint.setPathEffect(new CornerPathEffect(radius));
             arrowView.setBackground(arrowDrawable);
             arrowView.setElevation(getElevation());
         }
@@ -527,56 +548,28 @@ public class PopupContainerWithArrow extends AbstractFloatingView
     }
 
     /**
-     * We need to handle touch events to prevent them from falling through to the workspace below.
+     * Updates the notification header to reflect the badge info. Since this can be called
+     * for any badge info (not necessarily the one associated with this app), we first
+     * check that the ItemInfo matches the one of this popup.
      */
-    @SuppressLint("ClickableViewAccessibility")
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        return true;
-    }
-
-    @Override
-    public boolean onTouch(View v, MotionEvent ev) {
-        // Touched a shortcut, update where it was touched so we can drag from there on long click.
-        switch (ev.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-            case MotionEvent.ACTION_MOVE:
-                mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY());
-                break;
+    public void updateNotificationHeader(BadgeInfo badgeInfo, ItemInfo originalItemInfo) {
+        if (originalItemInfo != mOriginalIcon.getTag()) {
+            return;
         }
-        return false;
+        updateNotificationHeader(badgeInfo, mOriginalIcon);
     }
 
-    public boolean onLongClick(View v) {
-        // Return early if this is not initiated from a touch or not the correct view
-        if (!v.isInTouchMode() || !(v.getParent() instanceof DeepShortcutView)) return false;
-        // Return early if global dragging is not enabled
-        if (!mLauncher.isDraggingEnabled()) return false;
-        // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
-        if (mLauncher.getDragController().isDragging()) return false;
-
-        // Long clicked on a shortcut.
-        mDeferContainerRemoval = true;
-        DeepShortcutView sv = (DeepShortcutView) v.getParent();
-        sv.setWillDrawIcon(false);
-
-        // Move the icon to align with the center-top of the touch point
-        mIconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
-        mIconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx;
-
-        DragView dv = mLauncher.getWorkspace().beginDragShared(
-                sv.getBubbleText(), this, sv.getFinalInfo(),
-                new ShortcutDragPreviewProvider(sv.getIconView(), mIconShift), new DragOptions());
-        dv.animateShift(-mIconShift.x, -mIconShift.y);
-
-        // TODO: support dragging from within folder without having to close it
-        AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_FOLDER);
-        return false;
+    private void updateNotificationHeader(BadgeInfo badgeInfo, BubbleTextView originalIcon) {
+        if (mNotificationItemView != null && badgeInfo != null) {
+            IconPalette palette = originalIcon.getIcon() instanceof FastBitmapDrawable
+                    ? ((FastBitmapDrawable) originalIcon.getIcon()).getIconPalette()
+                    : null;
+            mNotificationItemView.updateHeader(badgeInfo.getNotificationCount(), palette);
+        }
     }
 
     public void trimNotifications(Map<PackageUserKey, BadgeInfo> updatedBadges) {
-        final NotificationItemView notificationView = (NotificationItemView) findViewById(R.id.notification_view);
-        if (notificationView == null) {
+        if (mNotificationItemView == null) {
             return;
         }
         ItemInfo originalInfo = (ItemInfo) mOriginalIcon.getTag();
@@ -585,12 +578,11 @@ public class PopupContainerWithArrow extends AbstractFloatingView
             AnimatorSet removeNotification = LauncherAnimUtils.createAnimatorSet();
             final int duration = getResources().getInteger(
                     R.integer.config_removeNotificationViewDuration);
-            final int spacing = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_spacing);
-            removeNotification.play(animateTranslationYBy(notificationView.getHeight() + spacing,
-                    duration));
-            Animator reduceHeight = notificationView.createRemovalAnimation(duration);
+            final int spacing = getResources().getDimensionPixelSize(R.dimen.popup_items_spacing);
+            removeNotification.play(reduceNotificationViewHeight(
+                    mNotificationItemView.getHeight() + spacing, duration));
             final View removeMarginView = mIsAboveIcon ? getItemViewAt(getItemCount() - 2)
-                    : notificationView;
+                    : mNotificationItemView;
             if (removeMarginView != null) {
                 ValueAnimator removeMargin = ValueAnimator.ofFloat(1, 0).setDuration(duration);
                 removeMargin.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -602,48 +594,81 @@ public class PopupContainerWithArrow extends AbstractFloatingView
                 });
                 removeNotification.play(removeMargin);
             }
-            removeNotification.play(reduceHeight);
-            Animator fade = new LauncherViewPropertyAnimator(notificationView).alpha(0)
+            Animator fade = ObjectAnimator.ofFloat(mNotificationItemView, ALPHA, 0)
                     .setDuration(duration);
             fade.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    removeView(notificationView);
+                    removeView(mNotificationItemView);
+                    mNotificationItemView = null;
                     if (getItemCount() == 0) {
                         close(false);
                         return;
                     }
-                    View firstItem = getItemViewAt(mIsAboveIcon ? getItemCount() - 1 : 0);
-                    mArrow.setBackgroundTintList(firstItem.getBackgroundTintList());
                 }
             });
             removeNotification.play(fade);
             final long arrowScaleDuration = getResources().getInteger(
                     R.integer.config_deepShortcutArrowOpenDuration);
-            Animator hideArrow = new LauncherViewPropertyAnimator(mArrow)
-                    .scaleX(0).scaleY(0).setDuration(arrowScaleDuration);
+            Animator hideArrow = createArrowScaleAnim(0).setDuration(arrowScaleDuration);
             hideArrow.setStartDelay(0);
-            Animator showArrow = new LauncherViewPropertyAnimator(mArrow)
-                    .scaleX(1).scaleY(1).setDuration(arrowScaleDuration);
+            Animator showArrow = createArrowScaleAnim(1).setDuration(arrowScaleDuration);
             showArrow.setStartDelay((long) (duration - arrowScaleDuration * 1.5));
             removeNotification.playSequentially(hideArrow, showArrow);
             removeNotification.start();
             return;
         }
-        notificationView.trimNotifications(badgeInfo.getNotificationKeys());
+        mNotificationItemView.trimNotifications(NotificationKeyData.extractKeysOnly(
+                badgeInfo.getNotificationKeys()));
+    }
+
+    @Override
+    protected void onWidgetsBound() {
+        enableWidgets();
+    }
+
+    public boolean enableWidgets() {
+        return mShortcutsItemView != null && mShortcutsItemView.enableWidgets(
+                (ItemInfo) mOriginalIcon.getTag());
+    }
+
+    private ObjectAnimator createArrowScaleAnim(float scale) {
+        return LauncherAnimUtils.ofPropertyValuesHolder(
+                mArrow, new PropertyListBuilder().scale(scale).build());
     }
 
     /**
-     * Animates the translationY of this container if it is open above the icon.
-     * If it is below the icon, the container already shifts up when the height
-     * of a child (e.g. NotificationView) changes, so the translation isn't necessary.
+     * Animates the height of the notification item and the translationY of other items accordingly.
      */
-    public @Nullable Animator animateTranslationYBy(int translationY, int duration) {
+    public Animator reduceNotificationViewHeight(int heightToRemove, int duration) {
+        final int translateYBy = mIsAboveIcon ? heightToRemove : -heightToRemove;
+        AnimatorSet animatorSet = LauncherAnimUtils.createAnimatorSet();
+        animatorSet.play(mNotificationItemView.animateHeightRemoval(heightToRemove));
+        PropertyResetListener<View, Float> resetTranslationYListener
+                = new PropertyResetListener<>(TRANSLATION_Y, 0f);
+        for (int i = 0; i < getItemCount(); i++) {
+            final PopupItemView itemView = getItemViewAt(i);
+            if (!mIsAboveIcon && itemView == mNotificationItemView) {
+                // The notification view is already in the right place when container is below icon.
+                continue;
+            }
+            ValueAnimator translateItem = ObjectAnimator.ofFloat(itemView, TRANSLATION_Y,
+                    itemView.getTranslationY() + translateYBy).setDuration(duration);
+            translateItem.addListener(resetTranslationYListener);
+            animatorSet.play(translateItem);
+        }
         if (mIsAboveIcon) {
-            return new LauncherViewPropertyAnimator(this)
-                    .translationY(getTranslationY() + translationY).setDuration(duration);
+            // All the items, including the notification item, translated down, but the
+            // container itself did not. This means the items would jump back to their
+            // original translation unless we update the container's translationY here.
+            animatorSet.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    setTranslationY(getTranslationY() + translateYBy);
+                }
+            });
         }
-        return null;
+        return animatorSet;
     }
 
     @Override
@@ -675,6 +700,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView
     public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
         // 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.
+        mDeferContainerRemoval = true;
         animateClose();
     }
 
@@ -696,7 +722,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView
     @Override
     public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
         target.itemType = ItemType.DEEPSHORTCUT;
-        target.rank = info.rank;
         targetParent.containerType = ContainerType.DEEPSHORTCUTS;
     }
 
@@ -738,37 +763,17 @@ public class PopupContainerWithArrow extends AbstractFloatingView
         for (int i = firstOpenItemIndex; i < firstOpenItemIndex + numOpenShortcuts; i++) {
             final PopupItemView view = getItemViewAt(i);
             Animator anim;
-            if (view.willDrawIcon()) {
-                anim = view.createCloseAnimation(mIsAboveIcon, mIsLeftAligned, duration);
-                int animationIndex = mIsAboveIcon ? i - firstOpenItemIndex
-                        : numOpenShortcuts - i - 1;
-                anim.setStartDelay(stagger * animationIndex);
-
-                Animator fadeAnim = new LauncherViewPropertyAnimator(view).alpha(0);
-                // Don't start fading until the arrow is gone.
-                fadeAnim.setStartDelay(stagger * animationIndex + arrowScaleDuration);
-                fadeAnim.setDuration(duration - arrowScaleDuration);
-                fadeAnim.setInterpolator(fadeInterpolator);
-                shortcutAnims.play(fadeAnim);
-            } else {
-                // The view is being dragged. Animate it such that it collapses with the drag view
-                anim = view.collapseToIcon();
-                anim.setDuration(DragView.VIEW_ZOOM_DURATION);
-
-                // Scale and translate the view to follow the drag view.
-                Point iconCenter = view.getIconCenter();
-                view.setPivotX(iconCenter.x);
-                view.setPivotY(iconCenter.y);
-
-                float scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / view.getHeight();
-                LauncherViewPropertyAnimator anim2 = new LauncherViewPropertyAnimator(view)
-                        .scaleX(scale)
-                        .scaleY(scale)
-                        .translationX(mIconShift.x)
-                        .translationY(mIconShift.y);
-                anim2.setDuration(DragView.VIEW_ZOOM_DURATION);
-                shortcutAnims.play(anim2);
-            }
+            anim = view.createCloseAnimation(mIsAboveIcon, mIsLeftAligned, duration);
+            int animationIndex = mIsAboveIcon ? i - firstOpenItemIndex
+                    : numOpenShortcuts - i - 1;
+            anim.setStartDelay(stagger * animationIndex);
+
+            Animator fadeAnim = ObjectAnimator.ofFloat(view, View.ALPHA, 0);
+            // Don't start fading until the arrow is gone.
+            fadeAnim.setStartDelay(stagger * animationIndex + arrowScaleDuration);
+            fadeAnim.setDuration(duration - arrowScaleDuration);
+            fadeAnim.setInterpolator(fadeInterpolator);
+            shortcutAnims.play(fadeAnim);
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
@@ -777,8 +782,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView
             });
             shortcutAnims.play(anim);
         }
-        Animator arrowAnim = new LauncherViewPropertyAnimator(mArrow)
-                .scaleX(0).scaleY(0).setDuration(arrowScaleDuration);
+        Animator arrowAnim = createArrowScaleAnim(0).setDuration(arrowScaleDuration);
         arrowAnim.setStartDelay(0);
         shortcutAnims.play(arrowAnim);