From 47c91b21fcbf85c20d59b5c3ca9d98bda0330cd0 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 14 Feb 2024 07:27:53 +0000 Subject: 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 --- core/java/android/window/IGlobalDragListener.aidl | 7 ++++ .../com/android/wm/shell/ShellTaskOrganizer.java | 13 ------- .../com/android/wm/shell/dagger/WMShellModule.java | 7 ++-- .../shell/draganddrop/DragAndDropController.java | 19 ++++++++++ .../wm/shell/draganddrop/GlobalDragListener.kt | 24 +++++++++++-- .../android/wm/shell/transition/Transitions.java | 6 +++- .../draganddrop/DragAndDropControllerTest.java | 8 +++-- .../wm/shell/draganddrop/GlobalDragListenerTest.kt | 40 ++++++++++++++-------- .../com/android/server/wm/DragDropController.java | 16 ++++++++- 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; @@ -24,6 +25,12 @@ import android.window.IUnhandledDragCallback; * {@hide} */ 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 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, + GlobalDragListener.GlobalDragListenerCallback, DisplayController.OnDisplaysChangedListener, View.OnDragListener, ComponentCallbacks2 { @@ -90,6 +94,8 @@ public class DragAndDropController implements RemoteCallable mListeners = new ArrayList<>(); @@ -112,6 +118,8 @@ public class DragAndDropController implements RemoteCallable, 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() + private val mMainExecutor = mock() 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() 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() 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() mController.onUnhandledDrop(dropEvent, wmCallback) verify(wmCallback).notifyUnhandledDropComplete(ArgumentMatchers.eq(true)) verify(dragSurface).release() assertEquals(lastDragEvent.get(0), dropEvent) } + + @Test + fun onCrossWindowDrop() { + val lastTaskInfo = arrayOfNulls(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() + 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; } } -- cgit v1.2.3-59-g8ed1b