Disable Drag from Taskbar in Overview

* Still need to disable drag from all apps in overview
* Disallow any taskbar icon long click when in split select

Bug: 251747761
Test: Drag from hotseat and predicted icons in overview,
snaps back to position

Change-Id: Ib9b068e4914b9197614c8e8f49b7899bb964f92b
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 9a1e064..d7bb16e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -18,10 +18,12 @@
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.annotation.NonNull;
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Intent;
@@ -49,7 +51,6 @@
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.config.FeatureFlags;
@@ -70,6 +71,7 @@
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.quickstep.util.LogUtils;
+import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.systemui.shared.recents.model.Task;
 
 import java.io.PrintWriter;
@@ -83,7 +85,8 @@
 public class TaskbarDragController extends DragController<BaseTaskbarContext> implements
         TaskbarControllers.LoggableTaskbarController {
 
-    private static boolean DEBUG_DRAG_SHADOW_SURFACE = false;
+    private static final boolean DEBUG_DRAG_SHADOW_SURFACE = false;
+    private static final int ANIM_DURATION_RETURN_ICON_TO_TASKBAR = 300;
 
     private final int mDragIconSize;
     private final int[] mTempXY = new int[2];
@@ -99,6 +102,8 @@
 
     // Animation for the drag shadow back into position after an unsuccessful drag
     private ValueAnimator mReturnAnimator;
+    private boolean mDisallowGlobalDrag;
+    private boolean mDisallowLongClick;
 
     public TaskbarDragController(BaseTaskbarContext activity) {
         super(activity);
@@ -110,6 +115,14 @@
         mControllers = controllers;
     }
 
+    public void setDisallowGlobalDrag(boolean disallowGlobalDrag) {
+        mDisallowGlobalDrag = disallowGlobalDrag;
+    }
+
+    public void setDisallowLongClick(boolean disallowLongClick) {
+        mDisallowLongClick = disallowLongClick;
+    }
+
     /**
      * Attempts to start a system drag and drop operation for the given View, using its tag to
      * generate the ClipDescription and Intent.
@@ -131,7 +144,7 @@
             View view,
             @Nullable DragPreviewProvider dragPreviewProvider,
             @Nullable Point iconShift) {
-        if (!(view instanceof BubbleTextView)) {
+        if (!(view instanceof BubbleTextView) || mDisallowLongClick) {
             return false;
         }
         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onTaskbarItemLongClick");
@@ -293,6 +306,7 @@
     }
 
     private void startSystemDrag(BubbleTextView btv) {
+        if (mDisallowGlobalDrag) return;
         View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(btv) {
 
             @Override
@@ -422,6 +436,45 @@
     }
 
     @Override
+    protected void endDrag() {
+        if (mDisallowGlobalDrag) {
+            // We need to explicitly set deferDragViewCleanupPostAnimation to true here so the
+            // super call doesn't remove it from the drag layer before the animation completes.
+            // This variable gets set in to false in super.dispatchDropComplete() because it
+            // (rightfully so, perhaps) thinks this drag operation has failed, and does its own
+            // internal cleanup.
+            // Another way to approach this would be to make all of overview a drop target and
+            // accept the drop as successful and then run the setupReturnDragAnimator to simulate
+            // drop failure to the user
+            mDragObject.deferDragViewCleanupPostAnimation = true;
+
+            float fromX = mDragObject.x - mDragObject.xOffset;
+            float fromY = mDragObject.y - mDragObject.yOffset;
+            DragView dragView = mDragObject.dragView;
+            setupReturnDragAnimator(fromX, fromY, (View) mDragObject.originalView,
+                    (x, y, scale, alpha) -> {
+                        dragView.setTranslationX(x);
+                        dragView.setTranslationY(y);
+                        dragView.setScaleX(scale);
+                        dragView.setScaleY(scale);
+                        dragView.setAlpha(alpha);
+                    });
+            mReturnAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    callOnDragEnd();
+                    dragView.remove();
+                    dragView.clearAnimation();
+                    mReturnAnimator = null;
+
+                }
+            });
+            mReturnAnimator.start();
+        }
+        super.endDrag();
+    }
+
+    @Override
     protected void callOnDragEnd() {
         super.callOnDragEnd();
         maybeOnDragEnd();
@@ -432,56 +485,20 @@
         SurfaceControl dragSurface = dragEvent.getDragSurface();
 
         // For top level icons, the target is the icon itself
-        View target = btv;
-        Object tag = btv.getTag();
-        if (tag instanceof ItemInfo) {
-            ItemInfo item = (ItemInfo) tag;
-            TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
-            if (item.container == CONTAINER_ALL_APPS || item.container == CONTAINER_PREDICTION) {
-                // Since all apps closes when the drag starts, target the all apps button instead.
-                target = taskbarViewController.getAllAppsButtonView();
-            } else if (item.container >= 0) {
-                // Since folders close when the drag starts, target the folder icon instead.
-                Predicate<ItemInfo> matcher = ItemInfoMatcher.forFolderMatch(
-                        ItemInfoMatcher.ofItemIds(IntSet.wrap(item.id)));
-                target = taskbarViewController.getFirstIconMatch(matcher);
-            } else if (item.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
-                // Find first icon with same package/user as the deep shortcut.
-                Predicate<ItemInfo> packageUserMatcher = ItemInfoMatcher.ofPackages(
-                        Collections.singleton(item.getTargetPackage()), item.user);
-                target = taskbarViewController.getFirstIconMatch(packageUserMatcher);
-            }
-        }
-
-        // Finish any pending return animation before starting a new drag
-        if (mReturnAnimator != null) {
-            mReturnAnimator.end();
-        }
+        View target = findTaskbarTargetForIconView(btv);
 
         float fromX = dragEvent.getX() - dragEvent.getOffsetX();
         float fromY = dragEvent.getY() - dragEvent.getOffsetY();
-        int[] toPosition = target.getLocationOnScreen();
-        float toScale = (float) target.getWidth() / mDragIconSize;
-        float toAlpha = (target == btv) ? 1f : 0f;
         final ViewRootImpl viewRoot = target.getViewRootImpl();
         SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
-        mReturnAnimator = ValueAnimator.ofFloat(0f, 1f);
-        mReturnAnimator.setDuration(300);
-        mReturnAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mReturnAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                float t = animation.getAnimatedFraction();
-                float accelT = Interpolators.ACCEL_2.getInterpolation(t);
-                float scale = 1f - t * (1f - toScale);
-                float alpha = 1f - accelT * (1f - toAlpha);
-                tx.setPosition(dragSurface, Utilities.mapRange(t, fromX, toPosition[0]),
-                        Utilities.mapRange(t, fromY, toPosition[1]));
-                tx.setScale(dragSurface, scale, scale);
-                tx.setAlpha(dragSurface, alpha);
-                tx.apply();
-            }
-        });
+        setupReturnDragAnimator(fromX, fromY, btv,
+                (x, y, scale, alpha) -> {
+                    tx.setPosition(dragSurface, x, y);
+                    tx.setScale(dragSurface, scale, scale);
+                    tx.setAlpha(dragSurface, alpha);
+                    tx.apply();
+                });
+
         mReturnAnimator.addListener(new AnimatorListenerAdapter() {
             private boolean mCanceled = false;
 
@@ -517,6 +534,63 @@
         mReturnAnimator.start();
     }
 
+    private View findTaskbarTargetForIconView(@NonNull View iconView) {
+        Object tag = iconView.getTag();
+        if (tag instanceof ItemInfo) {
+            ItemInfo item = (ItemInfo) tag;
+            TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
+            if (item.container == CONTAINER_ALL_APPS || item.container == CONTAINER_PREDICTION) {
+                // Since all apps closes when the drag starts, target the all apps button instead.
+                return taskbarViewController.getAllAppsButtonView();
+            } else if (item.container >= 0) {
+                // Since folders close when the drag starts, target the folder icon instead.
+                Predicate<ItemInfo> matcher = ItemInfoMatcher.forFolderMatch(
+                        ItemInfoMatcher.ofItemIds(IntSet.wrap(item.id)));
+                return taskbarViewController.getFirstIconMatch(matcher);
+            } else if (item.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
+                // Find first icon with same package/user as the deep shortcut.
+                Predicate<ItemInfo> packageUserMatcher = ItemInfoMatcher.ofPackages(
+                        Collections.singleton(item.getTargetPackage()), item.user);
+                return taskbarViewController.getFirstIconMatch(packageUserMatcher);
+            }
+        }
+        return iconView;
+    }
+
+    private void setupReturnDragAnimator(float fromX, float fromY, View originalView,
+            TaskbarReturnPropertiesListener animListener) {
+        // Finish any pending return animation before starting a new return
+        if (mReturnAnimator != null) {
+            mReturnAnimator.end();
+        }
+
+        // For top level icons, the target is the icon itself
+        View target = findTaskbarTargetForIconView(originalView);
+
+        int[] toPosition = target.getLocationOnScreen();
+        float toScale = (float) target.getWidth() / mDragIconSize;
+        float toAlpha = (target == originalView) ? 1f : 0f;
+        MultiValueUpdateListener listener = new MultiValueUpdateListener() {
+            final FloatProp mDx = new FloatProp(fromX, toPosition[0], 0,
+                    ANIM_DURATION_RETURN_ICON_TO_TASKBAR, Interpolators.FAST_OUT_SLOW_IN);
+            final FloatProp mDy = new FloatProp(fromY, toPosition[1], 0,
+                    ANIM_DURATION_RETURN_ICON_TO_TASKBAR,
+                    FAST_OUT_SLOW_IN);
+            final FloatProp mScale = new FloatProp(1f, toScale, 0,
+                    ANIM_DURATION_RETURN_ICON_TO_TASKBAR, FAST_OUT_SLOW_IN);
+            final FloatProp mAlpha = new FloatProp(1f, toAlpha, 0,
+                    ANIM_DURATION_RETURN_ICON_TO_TASKBAR, Interpolators.ACCEL_2);
+            @Override
+            public void onUpdate(float percent, boolean initOnly) {
+                animListener.updateDragShadow(mDx.value, mDy.value, mScale.value, mAlpha.value);
+            }
+        };
+        mReturnAnimator = ValueAnimator.ofFloat(0f, 1f);
+        mReturnAnimator.setDuration(ANIM_DURATION_RETURN_ICON_TO_TASKBAR);
+        mReturnAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mReturnAnimator.addUpdateListener(listener);
+    }
+
     @Override
     protected float getX(MotionEvent ev) {
         // We will resize to fill the screen while dragging, so use screen coordinates. This ensures
@@ -540,7 +614,7 @@
 
     @Override
     protected void exitDrag() {
-        if (mDragObject != null) {
+        if (mDragObject != null && !mDisallowGlobalDrag) {
             mActivity.getDragLayer().removeView(mDragObject.dragView);
         }
     }
@@ -556,6 +630,10 @@
         return null;
     }
 
+    interface TaskbarReturnPropertiesListener {
+        void updateDragShadow(float x, float y, float scale, float alpha);
+    }
+
     @Override
     public void dumpLogs(String prefix, PrintWriter pw) {
         pw.println(prefix + "TaskbarDragController:");
@@ -566,5 +644,7 @@
         pw.println(prefix + "\tmRegistrationY=" + mRegistrationY);
         pw.println(prefix + "\tmIsSystemDragInProgress=" + mIsSystemDragInProgress);
         pw.println(prefix + "\tisInternalDragInProgess=" + super.isDragging());
+        pw.println(prefix + "\tmDisallowGlobalDrag=" + mDisallowGlobalDrag);
+        pw.println(prefix + "\tmDisallowLongClick=" + mDisallowLongClick);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 63f1486..bc5bcf5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -39,6 +39,8 @@
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
+import com.android.launcher3.uioverrides.states.OverviewState;
+import com.android.launcher3.util.MultiValueAlpha;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.RecentsAnimationCallbacks;
 import com.android.quickstep.RecentsAnimationController;
@@ -117,6 +119,10 @@
                     mLauncherState = finalState;
                     updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, false);
                     applyState();
+                    mControllers.taskbarDragController.setDisallowGlobalDrag(
+                            (finalState instanceof OverviewState));
+                    mControllers.taskbarDragController.setDisallowLongClick(
+                            finalState == LauncherState.OVERVIEW_SPLIT_SELECT);
                 }
             };