From 406bc6f24705df7e2fda03d709c106fe70ce6f1c Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Fri, 21 Oct 2022 11:25:07 -0700 Subject: [PATCH] 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 --- .../taskbar/TaskbarDragController.java | 178 +++++++++++++----- .../TaskbarLauncherStateController.java | 6 + 2 files changed, 135 insertions(+), 49 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java index 9a1e0642bb..d7bb16e7a2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -18,10 +18,12 @@ package com.android.launcher3.taskbar; 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.DragSource; 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.testing.shared.TestProtocol; 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 @@ import java.util.function.Predicate; public class TaskbarDragController extends DragController 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 @@ public class TaskbarDragController extends DragController im // 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 @@ public class TaskbarDragController extends DragController im 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 @@ public class TaskbarDragController extends DragController im 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 @@ public class TaskbarDragController extends DragController im } private void startSystemDrag(BubbleTextView btv) { + if (mDisallowGlobalDrag) return; View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(btv) { @Override @@ -421,6 +435,45 @@ public class TaskbarDragController extends DragController im } } + @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(); @@ -432,56 +485,20 @@ public class TaskbarDragController extends DragController im 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 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 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 @@ public class TaskbarDragController extends DragController im 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 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 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 @@ public class TaskbarDragController extends DragController im @Override protected void exitDrag() { - if (mDragObject != null) { + if (mDragObject != null && !mDisallowGlobalDrag) { mActivity.getDragLayer().removeView(mDragObject.dragView); } } @@ -556,6 +630,10 @@ public class TaskbarDragController extends DragController im 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 @@ public class TaskbarDragController extends DragController im 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 63f1486729..bc5bcf5b28 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.anim.AnimatorListeners; 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 @@ import java.util.StringJoiner; mLauncherState = finalState; updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, false); applyState(); + mControllers.taskbarDragController.setDisallowGlobalDrag( + (finalState instanceof OverviewState)); + mControllers.taskbarDragController.setDisallowLongClick( + finalState == LauncherState.OVERVIEW_SPLIT_SELECT); } };