From 39938cbc9443b622df422d4a4772dd161e996774 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Fri, 26 Feb 2021 09:15:31 -0800 Subject: [PATCH] Initiate Hotseat drag from long pressing corresponding Taskbar item When you long press on the taskbar hotseat item, the following happens: - We start a system drag and drop with an invisible drag shadow - We create a new DragOptions with the simulatedDndStartPoint set to the drag down position, and tell Launcher to use that for the next drag - We perform a long click on the equivalent Hotseat item in Launcher - We pass the drag events of that operation to Launcher's DragController This allows Launcher to handle the entire drag operation, including the pre-drag (with popup), and taskbar already hides when the drag starts. Test: Long press items in taskbar hotseat, able to drag them to workspace Bug: 179886115 Bug: 171917176 Change-Id: I576b80cb1bd0225cdc91cf7689fdee0481265109 --- .../launcher3/BaseQuickstepLauncher.java | 12 +++ .../HotseatPredictionController.java | 3 +- .../launcher3/taskbar/TaskbarController.java | 9 +- .../taskbar/TaskbarDragController.java | 35 ++++++- .../taskbar/TaskbarDragListener.java | 99 +++++++++++++++++++ src/com/android/launcher3/Hotseat.java | 8 ++ src/com/android/launcher3/Launcher.java | 5 + src/com/android/launcher3/Workspace.java | 2 +- .../touch/ItemLongClickListener.java | 2 +- 9 files changed, 170 insertions(+), 5 deletions(-) create mode 100644 quickstep/src/com/android/launcher3/taskbar/TaskbarDragListener.java diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 5b301433c3..161c98ec5f 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -35,6 +35,7 @@ import android.view.View; import androidx.annotation.Nullable; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.proxy.ProxyActivityStarter; @@ -83,6 +84,8 @@ public abstract class BaseQuickstepLauncher extends Launcher private @Nullable TaskbarController mTaskbarController; private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this); + // Will be updated when dragging from taskbar. + private DragOptions mWorkspaceDragOptions = new DragOptions(); @Override protected void onCreate(Bundle savedInstanceState) { @@ -269,6 +272,15 @@ public abstract class BaseQuickstepLauncher extends Launcher return mTaskbarController != null && mTaskbarController.isViewInTaskbar(v); } + @Override + public DragOptions getDefaultWorkspaceDragOptions() { + return mWorkspaceDragOptions; + } + + public void setWorkspaceDragOptions(DragOptions dragOptions) { + mWorkspaceDragOptions = dragOptions; + } + @Override public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { QuickstepAppTransitionManagerImpl appTransitionManager = diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index 0156e8f513..f297343552 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -107,7 +107,8 @@ public class HotseatPredictionController implements DragController.DragListener, WorkspaceItemInfo dragItem = new WorkspaceItemInfo((WorkspaceItemInfo) v.getTag()); v.setVisibility(View.INVISIBLE); mLauncher.getWorkspace().beginDragShared( - v, null, this, dragItem, new DragPreviewProvider(v), new DragOptions()); + v, null, this, dragItem, new DragPreviewProvider(v), + mLauncher.getDefaultWorkspaceDragOptions()); return true; }; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java index 544bc99869..74a82ab4e2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java @@ -170,7 +170,14 @@ public class TaskbarController { @Override public View.OnLongClickListener getItemOnLongClickListener() { - return mDragController::startDragOnLongClick; + return view -> { + if (mLauncher.hasBeenResumed() && view.getTag() instanceof ItemInfo) { + alignRealHotseatWithTaskbar(); + return mDragController.startWorkspaceDragOnLongClick(view); + } else { + return mDragController.startSystemDragOnLongClick(view); + } + }; } @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java index baec8998f3..f51e49837d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -33,6 +33,7 @@ import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.BubbleTextView; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; +import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ClipDescriptionCompat; @@ -57,7 +58,7 @@ public class TaskbarDragController { * generate the ClipDescription and Intent. * @return Whether {@link View#startDragAndDrop} started successfully. */ - protected boolean startDragOnLongClick(View view) { + protected boolean startSystemDragOnLongClick(View view) { if (!(view instanceof BubbleTextView)) { return false; } @@ -124,6 +125,38 @@ public class TaskbarDragController { return false; } + /** + * Starts a drag and drop operation that controls a real Workspace (Hotseat) view. + * @param view The Taskbar item that was long clicked. + * @return Whether {@link View#startDragAndDrop} started successfully. + */ + protected boolean startWorkspaceDragOnLongClick(View view) { + View.DragShadowBuilder transparentShadowBuilder = new View.DragShadowBuilder(view) { + private static final int ARBITRARY_SHADOW_SIZE = 10; + + @Override + public void onDrawShadow(Canvas canvas) { + } + + @Override + public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) { + outShadowSize.set(ARBITRARY_SHADOW_SIZE, ARBITRARY_SHADOW_SIZE); + outShadowTouchPoint.set(ARBITRARY_SHADOW_SIZE / 2, ARBITRARY_SHADOW_SIZE / 2); + } + }; + + TaskbarDragListener taskbarDragListener = new TaskbarDragListener(mLauncher, + (ItemInfo) view.getTag()); + if (view.startDragAndDrop(new ClipData("", new String[] {taskbarDragListener.getMimeType()}, + new ClipData.Item("")), + transparentShadowBuilder, null /* localState */, View.DRAG_FLAG_GLOBAL)) { + view.setOnDragListener(getDraggedViewDragListener()); + taskbarDragListener.init(mLauncher.getDragLayer()); + return true; + } + return false; + } + /** * Hide the original Taskbar item while it is being dragged. */ diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragListener.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragListener.java new file mode 100644 index 0000000000..2bd58617f0 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragListener.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.taskbar; + +import android.content.ClipDescription; +import android.graphics.Point; +import android.view.DragEvent; +import android.view.View; + +import com.android.launcher3.BaseQuickstepLauncher; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.model.data.ItemInfo; + +import java.util.UUID; + +/** + * Listens to system drag and drop events initated by the Taskbar, and forwards them to Launcher's + * internal DragController to move Hotseat items. + */ +public class TaskbarDragListener implements View.OnDragListener { + + private static final String MIME_TYPE_PREFIX = "com.android.launcher3.taskbar.drag_and_drop/"; + + private final BaseQuickstepLauncher mLauncher; + private final ItemInfo mDraggedItem; + private final DragOptions mDragOptions; + // Randomly generated id used to verify the drag event. + private final String mId; + + // Initialized in init(). + DragLayer mDragLayer; + + /** + * @param draggedItem The info of the item that was long clicked, which we will use to find + * the equivalent match on Hotseat to drag internally. + */ + public TaskbarDragListener(BaseQuickstepLauncher launcher, ItemInfo draggedItem) { + mLauncher = launcher; + mDraggedItem = draggedItem; + mDragOptions = new DragOptions(); + mDragOptions.simulatedDndStartPoint = new Point(); + mId = UUID.randomUUID().toString(); + } + + protected void init(DragLayer dragLayer) { + mDragLayer = dragLayer; + mDragLayer.setOnDragListener(this); + } + + private void cleanup() { + mDragLayer.setOnDragListener(null); + mLauncher.setWorkspaceDragOptions(new DragOptions()); + } + + /** + * Returns a randomly generated id used to verify the drag event. + */ + protected String getMimeType() { + return MIME_TYPE_PREFIX + mId; + } + + @Override + public boolean onDrag(View dragLayer, DragEvent dragEvent) { + ClipDescription clipDescription = dragEvent.getClipDescription(); + if (dragEvent.getAction() == DragEvent.ACTION_DRAG_STARTED) { + if (clipDescription == null || !clipDescription.hasMimeType(getMimeType())) { + // We didn't initiate this drag, ignore. + cleanup(); + return false; + } + View hotseatView = mLauncher.getHotseat().getFirstItemMatch( + (info, view) -> info == mDraggedItem); + if (hotseatView == null) { + cleanup(); + return false; + } + mDragOptions.simulatedDndStartPoint.set((int) dragEvent.getX(), (int) dragEvent.getY()); + mLauncher.setWorkspaceDragOptions(mDragOptions); + hotseatView.performLongClick(); + } else if (dragEvent.getAction() == DragEvent.ACTION_DRAG_ENDED) { + cleanup(); + } + return mLauncher.getDragController().onDragEvent(dragEvent); + } +} diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index ebaacb67d0..b2112ad83a 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -21,6 +21,7 @@ import android.graphics.Rect; import android.util.AttributeSet; import android.view.Gravity; import android.view.MotionEvent; +import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -166,4 +167,11 @@ public class Hotseat extends CellLayout implements Insettable { protected void showInlineQsb() { //Does nothing } + + /** + * Returns the first View for which the given itemOperator returns true, or null. + */ + public View getFirstItemMatch(Workspace.ItemOperator itemOperator) { + return mWorkspace.getFirstMatch(new CellLayout[] { this }, itemOperator); + } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 253a7c7086..fa63885c67 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -121,6 +121,7 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dot.DotInfo; 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.folder.FolderGridOrganizer; import com.android.launcher3.folder.FolderIcon; @@ -2863,6 +2864,10 @@ public class Launcher extends StatefulActivity implements Launche return false; } + public DragOptions getDefaultWorkspaceDragOptions() { + return new DragOptions(); + } + private static class NonConfigInstance { public Configuration config; public Bitmap snapshot; diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 77fee08de2..87fb6fb7a1 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2973,7 +2973,7 @@ public class Workspace extends PagedView * @param operators List of operators, in order starting from best matching operator. * @return */ - private View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator... operators) { + View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator... operators) { // This array is filled with the first match for each operator. final View[] matches = new View[operators.length]; // For efficiency, the outer loop should be CellLayout. diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java index 919673f525..f876dd9230 100644 --- a/src/com/android/launcher3/touch/ItemLongClickListener.java +++ b/src/com/android/launcher3/touch/ItemLongClickListener.java @@ -57,7 +57,7 @@ public class ItemLongClickListener { if (!(v.getTag() instanceof ItemInfo)) return false; launcher.setWaitingForResult(null); - beginDrag(v, launcher, (ItemInfo) v.getTag(), new DragOptions()); + beginDrag(v, launcher, (ItemInfo) v.getTag(), launcher.getDefaultWorkspaceDragOptions()); return true; }