diff options
| author | 2024-02-14 07:27:53 +0000 | |
|---|---|---|
| committer | 2024-02-21 19:24:23 +0000 | |
| commit | 47c91b21fcbf85c20d59b5c3ca9d98bda0330cd0 (patch) | |
| tree | ebfdf2afa4f98e413db1e29c1b57afb6b0bb573c | |
| parent | b4d9b79edb1060f7c40febf0da10f44c2d74d041 (diff) | |
Bring the task forward when it consumes a cross-window global drag
- Send a notification when a successful cross-window drag occurs over a
task so that we can bring it forward
Bug: 320797628
Test: atest DragDropTest DragDropControllerTests CrossAppDragAndDropTests
Test: atest WMShellUnitTests
Change-Id: Id2d2258a1e71b6dbc9a57e2f58ef43825cfc18c7
9 files changed, 105 insertions, 35 deletions
diff --git a/core/java/android/window/IGlobalDragListener.aidl b/core/java/android/window/IGlobalDragListener.aidl index 81388559f8dc..8f2ca02b3c09 100644 --- a/core/java/android/window/IGlobalDragListener.aidl +++ b/core/java/android/window/IGlobalDragListener.aidl @@ -16,6 +16,7 @@ package android.window; +import android.app.ActivityManager; import android.view.DragEvent; import android.window.IUnhandledDragCallback; @@ -25,6 +26,12 @@ import android.window.IUnhandledDragCallback; */ oneway interface IGlobalDragListener { /** + * Called when a cross-window drag is handled by another window. + * @param taskInfo the task containing the window that consumed the drop + */ + void onCrossWindowDrop(in ActivityManager.RunningTaskInfo taskInfo); + + /** * Called when the user finishes the drag gesture but no windows have reported handling the * drop. The DragEvent is populated with the drag surface for the listener to animate. The * listener *MUST* call the provided callback exactly once when it has finished handling the diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index fe65fdd30e48..d8d0d876b4f2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -643,19 +643,6 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } } - /** Helper to set int metadata on the Surface corresponding to the task id. */ - public void setSurfaceMetadata(int taskId, int key, int value) { - synchronized (mLock) { - final TaskAppearedInfo info = mTasks.get(taskId); - if (info == null || info.getLeash() == null) { - return; - } - SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - t.setMetadata(info.getLeash(), key, value); - t.apply(); - } - } - private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash, TaskListener oldListener, TaskListener newListener) { if (oldListener == newListener) return false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 34359a523cd8..aded1153d778 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -577,9 +577,12 @@ public abstract class WMShellModule { DisplayController displayController, UiEventLogger uiEventLogger, IconProvider iconProvider, + GlobalDragListener globalDragListener, + Transitions transitions, @ShellMainThread ShellExecutor mainExecutor) { - return new DragAndDropController(context, shellInit, shellController, - shellCommandHandler, displayController, uiEventLogger, iconProvider, mainExecutor); + return new DragAndDropController(context, shellInit, shellController, shellCommandHandler, + displayController, uiEventLogger, iconProvider, globalDragListener, transitions, + mainExecutor); } // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index 269c3699ac0a..0fea3e3e219c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -35,6 +35,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP; +import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.content.ClipDescription; import android.content.ComponentCallbacks2; @@ -51,6 +52,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; +import android.window.WindowContainerTransaction; import androidx.annotation.BinderThread; import androidx.annotation.NonNull; @@ -71,6 +73,7 @@ import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; import java.util.ArrayList; @@ -79,6 +82,7 @@ import java.util.ArrayList; * Handles the global drag and drop handling for the Shell. */ public class DragAndDropController implements RemoteCallable<DragAndDropController>, + GlobalDragListener.GlobalDragListenerCallback, DisplayController.OnDisplaysChangedListener, View.OnDragListener, ComponentCallbacks2 { @@ -90,6 +94,8 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll private final DisplayController mDisplayController; private final DragAndDropEventLogger mLogger; private final IconProvider mIconProvider; + private final GlobalDragListener mGlobalDragListener; + private final Transitions mTransitions; private SplitScreenController mSplitScreen; private ShellExecutor mMainExecutor; private ArrayList<DragAndDropListener> mListeners = new ArrayList<>(); @@ -112,6 +118,8 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll DisplayController displayController, UiEventLogger uiEventLogger, IconProvider iconProvider, + GlobalDragListener globalDragListener, + Transitions transitions, ShellExecutor mainExecutor) { mContext = context; mShellController = shellController; @@ -119,6 +127,8 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll mDisplayController = displayController; mLogger = new DragAndDropEventLogger(uiEventLogger); mIconProvider = iconProvider; + mGlobalDragListener = globalDragListener; + mTransitions = transitions; mMainExecutor = mainExecutor; shellInit.addInitCallback(this::onInit, this); } @@ -136,6 +146,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll mShellController.addExternalInterface(KEY_EXTRA_SHELL_DRAG_AND_DROP, this::createExternalInterface, this); mShellCommandHandler.addDumpCallback(this::dump, this); + mGlobalDragListener.setListener(this); } private ExternalInterfaceBinder createExternalInterface() { @@ -322,6 +333,14 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll return true; } + @Override + public void onCrossWindowDrop(@NonNull ActivityManager.RunningTaskInfo taskInfo) { + // Bring the task forward when an item is dropped on it + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.reorder(taskInfo.token, true /* onTop */); + mTransitions.startTransition(WindowManager.TRANSIT_TO_FRONT, wct, null); + } + /** * Handles dropping on the drop target. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt index eb352fcf1a8f..7f7cfb964e7d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt @@ -15,6 +15,7 @@ */ package com.android.wm.shell.draganddrop +import android.app.ActivityManager import android.os.RemoteException import android.util.Log import android.view.DragEvent @@ -31,13 +32,19 @@ import java.util.function.Consumer * Manages the listener and callbacks for unhandled global drags. */ class GlobalDragListener( - val wmService: IWindowManager, - mainExecutor: ShellExecutor + private val wmService: IWindowManager, + private val mainExecutor: ShellExecutor ) { private var callback: GlobalDragListenerCallback? = null private val globalDragListener: IGlobalDragListener = object : IGlobalDragListener.Stub() { + override fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) { + mainExecutor.execute() { + this@GlobalDragListener.onCrossWindowDrop(taskInfo) + } + } + override fun onUnhandledDrop(event: DragEvent, callback: IUnhandledDragCallback) { mainExecutor.execute() { this@GlobalDragListener.onUnhandledDrop(event, callback) @@ -50,6 +57,11 @@ class GlobalDragListener( */ interface GlobalDragListenerCallback { /** + * Called when a global drag is successfully handled by another window. + */ + fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) {} + + /** * Called when a global drag is unhandled (ie. dropped outside of all visible windows, or * dropped on a window that does not want to handle it). * @@ -80,11 +92,19 @@ class GlobalDragListener( } @VisibleForTesting + fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "onCrossWindowDrop: %s", taskInfo) + callback?.onCrossWindowDrop(taskInfo) + } + + @VisibleForTesting fun onUnhandledDrop(dragEvent: DragEvent, wmCallback: IUnhandledDragCallback) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "onUnhandledDrop: %s", dragEvent) if (callback == null) { wmCallback.notifyUnhandledDropComplete(false) + return } callback?.onUnhandledDrop(dragEvent) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 5e79681e060b..8a0e7977178c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -1170,7 +1170,11 @@ public class Transitions implements RemoteCallable<Transitions>, mPendingTransitions.add(0, active); } - /** Start a new transition directly. */ + /** + * Start a new transition directly. + * @param handler if null, the transition will be dispatched to the registered set of transition + * handlers to be handled + */ public IBinder startTransition(@WindowManager.TransitionType int type, @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition " diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java index 54f36f61859d..a64ebd301c00 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java @@ -45,12 +45,14 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; import com.android.launcher3.icons.IconProvider; +import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.Transitions; import org.junit.Before; import org.junit.Test; @@ -84,7 +86,9 @@ public class DragAndDropControllerTest extends ShellTestCase { @Mock private ShellExecutor mMainExecutor; @Mock - private WindowManager mWindowManager; + private Transitions mTransitions; + @Mock + private GlobalDragListener mGlobalDragListener; private DragAndDropController mController; @@ -93,7 +97,7 @@ public class DragAndDropControllerTest extends ShellTestCase { MockitoAnnotations.initMocks(this); mController = new DragAndDropController(mContext, mShellInit, mShellController, mShellCommandHandler, mDisplayController, mUiEventLogger, mIconProvider, - mMainExecutor); + mGlobalDragListener, mTransitions, mMainExecutor); mController.onInit(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/GlobalDragListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/GlobalDragListenerTest.kt index 4c025984e095..e731b06c0947 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/GlobalDragListenerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/GlobalDragListenerTest.kt @@ -15,6 +15,7 @@ */ package com.android.wm.shell.draganddrop +import android.app.ActivityManager.RunningTaskInfo import android.os.RemoteException import android.view.DragEvent import android.view.DragEvent.ACTION_DROP @@ -32,12 +33,10 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers -import org.mockito.Mock import org.mockito.Mockito -import org.mockito.Mockito.mock -import org.mockito.Mockito.reset -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations +import org.mockito.kotlin.mock +import org.mockito.kotlin.reset +import org.mockito.kotlin.verify /** * Tests for the unhandled drag controller. @@ -45,18 +44,14 @@ import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) class UnhandledDragControllerTest : ShellTestCase() { - @Mock - private lateinit var mIWindowManager: IWindowManager - - @Mock - private lateinit var mMainExecutor: ShellExecutor + private val mIWindowManager = mock<IWindowManager>() + private val mMainExecutor = mock<ShellExecutor>() private lateinit var mController: GlobalDragListener @Before @Throws(RemoteException::class) fun setUp() { - MockitoAnnotations.initMocks(this) mController = GlobalDragListener(mIWindowManager, mMainExecutor) } @@ -81,7 +76,7 @@ class UnhandledDragControllerTest : ShellTestCase() { // Simulate an unhandled drop val dropEvent = DragEvent.obtain(ACTION_DROP, 0f, 0f, 0f, 0f, null, null, null, null, null, false) - val wmCallback = mock(IUnhandledDragCallback::class.java) + val wmCallback = mock<IUnhandledDragCallback>() mController.onUnhandledDrop(dropEvent, wmCallback) verify(wmCallback).notifyUnhandledDropComplete(ArgumentMatchers.eq(false)) @@ -102,14 +97,31 @@ class UnhandledDragControllerTest : ShellTestCase() { }) // Simulate an unhandled drop - val dragSurface = mock(SurfaceControl::class.java) + val dragSurface = mock<SurfaceControl>() val dropEvent = DragEvent.obtain(ACTION_DROP, 0f, 0f, 0f, 0f, null, null, null, dragSurface, null, false) - val wmCallback = mock(IUnhandledDragCallback::class.java) + val wmCallback = mock<IUnhandledDragCallback>() mController.onUnhandledDrop(dropEvent, wmCallback) verify(wmCallback).notifyUnhandledDropComplete(ArgumentMatchers.eq(true)) verify(dragSurface).release() assertEquals(lastDragEvent.get(0), dropEvent) } + + @Test + fun onCrossWindowDrop() { + val lastTaskInfo = arrayOfNulls<RunningTaskInfo>(1) + + // Set a listener to listen for unhandled drops + mController.setListener(object : GlobalDragListenerCallback { + override fun onCrossWindowDrop(taskInfo: RunningTaskInfo) { + lastTaskInfo[0] = taskInfo + } + }) + + // Simulate a cross-window drop + val taskInfo = mock<RunningTaskInfo>() + mController.onCrossWindowDrop(taskInfo) + assertEquals(lastTaskInfo.get(0), taskInfo) + } } diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index a83c25feb236..b68e67e291e7 100644 --- a/services/core/java/com/android/server/wm/DragDropController.java +++ b/services/core/java/com/android/server/wm/DragDropController.java @@ -25,6 +25,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACT import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; +import android.app.ActivityManager; import android.content.ClipData; import android.content.Context; import android.hardware.input.InputManagerGlobal; @@ -344,7 +345,20 @@ class DragDropController { final boolean relinquishDragSurfaceToDropTarget = consumed && mDragState.targetInterceptsGlobalDrag(callingWin); + final boolean isCrossWindowDrag = !mDragState.mLocalWin.equals(token); mDragState.endDragLocked(consumed, relinquishDragSurfaceToDropTarget); + + final Task droppedWindowTask = callingWin.getTask(); + if (com.android.window.flags.Flags.delegateUnhandledDrags() + && mGlobalDragListener != null && droppedWindowTask != null && consumed + && isCrossWindowDrag) { + try { + mGlobalDragListener.onCrossWindowDrop(droppedWindowTask.getTaskInfo()); + } catch (RemoteException e) { + Slog.e(TAG_WM, "Failed to call global drag listener for cross-window " + + "drop", e); + } + } } } finally { mCallback.get().postReportDropResult(); @@ -383,7 +397,7 @@ class DragDropController { }); return true; } catch (RemoteException e) { - Slog.e(TAG_WM, "Failed to call unhandled drag listener", e); + Slog.e(TAG_WM, "Failed to call global drag listener for unhandled drop", e); return false; } } |