From c5dfdd152061f6e766c47e14a4df50c9fad791a2 Mon Sep 17 00:00:00 2001 From: mattsziklay Date: Mon, 10 Apr 2023 13:24:52 -0700 Subject: Prevent desktop mode corner handles from triggering back gesture. Adds exclusion logic to EdgeBackGestureHandler to prevent back gestures when attempting to resize a desktop mode task. Bug: 269661917 Test: Manual; resize or drag task to side and resize a corner without triggering gesture. Change-Id: I32c4c3687a03ed8350bffaa13603c1b9cb23fb3c --- .../android/wm/shell/desktopmode/DesktopMode.java | 14 +++++- .../shell/desktopmode/DesktopModeController.java | 44 ++++++++++++++++-- .../shell/desktopmode/DesktopModeTaskRepository.kt | 52 ++++++++++++++++++++++ .../wm/shell/desktopmode/DesktopTasksController.kt | 48 ++++++++++++++++++-- .../DesktopModeWindowDecorViewModel.java | 25 ++++++++++- .../windowdecor/DesktopModeWindowDecoration.java | 37 ++++++++++++++- .../shell/windowdecor/DragResizeInputListener.java | 18 +++++++- 7 files changed, 225 insertions(+), 13 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java index cbd544cc4b86..e732a0354806 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java @@ -16,9 +16,12 @@ package com.android.wm.shell.desktopmode; +import android.graphics.Region; + import com.android.wm.shell.common.annotations.ExternalThread; import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Interface to interact with desktop mode feature in shell. @@ -32,7 +35,16 @@ public interface DesktopMode { * @param listener the listener to add. * @param callbackExecutor the executor to call the listener on. */ - void addListener(DesktopModeTaskRepository.VisibleTasksListener listener, + void addVisibleTasksListener(DesktopModeTaskRepository.VisibleTasksListener listener, Executor callbackExecutor); + /** + * Adds a consumer to listen for Desktop task corner changes. This is used for gesture + * exclusion. The SparseArray contains a list of four corner resize handles mapped to each + * desktop task's taskId. The resize handle Rects are stored in the following order: + * left-top, left-bottom, right-top, right-bottom. + */ + default void addDesktopGestureExclusionRegionListener(Consumer listener, + Executor callbackExecutor) { } + } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java index 2bdbde1b71d4..86ea72582a52 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java @@ -34,6 +34,7 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration; import android.content.Context; import android.database.ContentObserver; +import android.graphics.Region; import android.net.Uri; import android.os.Handler; import android.os.IBinder; @@ -69,6 +70,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Handles windowing changes when desktop mode system setting changes @@ -149,11 +151,21 @@ public class DesktopModeController implements RemoteCallable listener, + Executor callbackExecutor) { + mDesktopModeTaskRepository.setTaskCornerListener(listener, callbackExecutor); + } + @VisibleForTesting void updateDesktopModeActive(boolean active) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeActive: active=%s", active); @@ -311,6 +323,23 @@ public class DesktopModeController implements RemoteCallable { + DesktopModeController.this.addVisibleTasksListener(listener, callbackExecutor); + }); + } + + @Override + public void addDesktopGestureExclusionRegionListener(Consumer listener, Executor callbackExecutor) { mMainExecutor.execute(() -> { - DesktopModeController.this.addListener(listener, callbackExecutor); + DesktopModeController.this.addTaskCornerListener(listener, callbackExecutor); }); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt index 47342c9f21ee..12f8ea23ac8f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt @@ -16,9 +16,13 @@ package com.android.wm.shell.desktopmode +import android.graphics.Region import android.util.ArrayMap import android.util.ArraySet +import android.util.SparseArray +import androidx.core.util.valueIterator import java.util.concurrent.Executor +import java.util.function.Consumer /** * Keeps track of task data related to desktop mode. @@ -38,6 +42,10 @@ class DesktopModeTaskRepository { private val activeTasksListeners = ArraySet() // Track visible tasks separately because a task may be part of the desktop but not visible. private val visibleTasksListeners = ArrayMap() + // Track corners of desktop tasks, used to determine gesture exclusion + private val desktopCorners = SparseArray() + private var desktopGestureExclusionListener: Consumer? = null + private var desktopGestureExclusionExecutor: Executor? = null /** * Add a [ActiveTasksListener] to be notified of updates to active tasks in the repository. @@ -55,6 +63,28 @@ class DesktopModeTaskRepository { Runnable { visibleTasksListener.onVisibilityChanged(visibleTasks.size > 0) }) } + /** + * Add a Consumer which will inform other classes of changes to corners for all Desktop tasks. + */ + fun setTaskCornerListener(cornersListener: Consumer, executor: Executor) { + desktopGestureExclusionListener = cornersListener + desktopGestureExclusionExecutor = executor + executor.execute { + desktopGestureExclusionListener?.accept(calculateDesktopExclusionRegion()) + } + } + + /** + * Create a new merged region representative of all corners in all desktop tasks. + */ + private fun calculateDesktopExclusionRegion(): Region { + val desktopCornersRegion = Region() + desktopCorners.valueIterator().forEach { taskCorners -> + desktopCornersRegion.op(taskCorners, Region.Op.UNION) + } + return desktopCornersRegion + } + /** * Remove a previously registered [ActiveTasksListener] */ @@ -166,6 +196,28 @@ class DesktopModeTaskRepository { freeformTasksInZOrder.remove(taskId) } + /** + * Updates the active desktop corners; if desktopCorners has been accepted by + * desktopCornersListener, it will be updated in the appropriate classes. + */ + fun updateTaskCorners(taskId: Int, taskCorners: Region) { + desktopCorners.put(taskId, taskCorners) + desktopGestureExclusionExecutor?.execute { + desktopGestureExclusionListener?.accept(calculateDesktopExclusionRegion()) + } + } + + /** + * Removes the active desktop corners for the specified task; if desktopCorners has been + * accepted by desktopCornersListener, it will be updated in the appropriate classes. + */ + fun removeTaskCorners(taskId: Int) { + desktopCorners.delete(taskId) + desktopGestureExclusionExecutor?.execute { + desktopGestureExclusionListener?.accept(calculateDesktopExclusionRegion()) + } + } + /** * Defines interface for classes that can listen to changes for active tasks in desktop mode. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 0400963a47e8..0d5602365578 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -27,6 +27,7 @@ import android.app.WindowConfiguration.WindowingMode import android.content.Context import android.graphics.Point import android.graphics.Rect +import android.graphics.Region import android.os.IBinder import android.os.SystemProperties import android.view.SurfaceControl @@ -487,6 +488,20 @@ class DesktopTasksController( return 2 * getStatusBarHeight(taskInfo) } + /** + * Update the corner region for a specified task + */ + fun onTaskCornersChanged(taskId: Int, corner: Region) { + desktopModeTaskRepository.updateTaskCorners(taskId, corner) + } + + /** + * Remove a previously tracked corner region for a specified task. + */ + fun removeCornersForTask(taskId: Int) { + desktopModeTaskRepository.removeTaskCorners(taskId) + } + /** * Adds a listener to find out about changes in the visibility of freeform tasks. @@ -494,20 +509,47 @@ class DesktopTasksController( * @param listener the listener to add. * @param callbackExecutor the executor to call the listener on. */ - fun addListener(listener: VisibleTasksListener, callbackExecutor: Executor) { + fun addVisibleTasksListener(listener: VisibleTasksListener, callbackExecutor: Executor) { desktopModeTaskRepository.addVisibleTasksListener(listener, callbackExecutor) } + /** + * Adds a listener to track changes to desktop task corners + * + * @param listener the listener to add. + * @param callbackExecutor the executor to call the listener on. + */ + fun setTaskCornerListener( + listener: Consumer, + callbackExecutor: Executor + ) { + desktopModeTaskRepository.setTaskCornerListener(listener, callbackExecutor) + } + /** The interface for calls from outside the shell, within the host process. */ @ExternalThread private inner class DesktopModeImpl : DesktopMode { - override fun addListener(listener: VisibleTasksListener, callbackExecutor: Executor) { + override fun addVisibleTasksListener( + listener: VisibleTasksListener, + callbackExecutor: Executor + ) { mainExecutor.execute { - this@DesktopTasksController.addListener(listener, callbackExecutor) + this@DesktopTasksController.addVisibleTasksListener(listener, callbackExecutor) + } + } + + override fun addDesktopGestureExclusionRegionListener( + listener: Consumer, + callbackExecutor: Executor + ) { + mainExecutor.execute { + this@DesktopTasksController.setTaskCornerListener(listener, callbackExecutor) } } } + + /** The interface for calls from outside the host process. */ @BinderThread private class IDesktopModeImpl(private var controller: DesktopTasksController?) : diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 5226eeebcaa2..9ba7a0dafa43 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -36,6 +36,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.Region; import android.hardware.input.InputManager; import android.os.Handler; import android.os.IBinder; @@ -69,6 +70,7 @@ import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.TaskCornersListener; import java.util.Optional; import java.util.function.Supplier; @@ -95,9 +97,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private SparseArray mEventReceiversByDisplay = new SparseArray<>(); + private final TaskCornersListener mCornersListener = new TaskCornersListenerImpl(); + private final SparseArray mWindowDecorByTaskId = new SparseArray<>(); - private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl(); + private final DragListenerImpl mDragStartListener = new DragListenerImpl(); private final InputMonitorFactory mInputMonitorFactory; private TaskOperations mTaskOperations; private final Supplier mTransactionFactory; @@ -778,13 +782,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final DesktopModeTouchEventListener touchEventListener = new DesktopModeTouchEventListener(taskInfo, taskPositioner); windowDecoration.setCaptionListeners(touchEventListener, touchEventListener); + windowDecoration.setCornersListener(mCornersListener); windowDecoration.setDragPositioningCallback(taskPositioner); windowDecoration.setDragDetector(touchEventListener.mDragDetector); windowDecoration.relayout(taskInfo, startT, finishT); incrementEventReceiverTasks(taskInfo.displayId); } - private class DragStartListenerImpl implements TaskPositioner.DragStartListener { + private class DragListenerImpl implements TaskPositioner.DragStartListener { @Override public void onDragStart(int taskId) { mWindowDecorByTaskId.get(taskId).closeHandleMenu(); @@ -796,6 +801,22 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return inputManager.monitorGestureInput("caption-touch", context.getDisplayId()); } } + + private class TaskCornersListenerImpl + implements DesktopModeWindowDecoration.TaskCornersListener { + + @Override + public void onTaskCornersChanged(int taskId, Region corner) { + mDesktopModeController.ifPresent(d -> d.onTaskCornersChanged(taskId, corner)); + mDesktopTasksController.ifPresent(d -> d.onTaskCornersChanged(taskId, corner)); + } + + @Override + public void onTaskCornersRemoved(int taskId) { + mDesktopModeController.ifPresent(d -> d.removeCornersForTask(taskId)); + mDesktopTasksController.ifPresent(d -> d.removeCornersForTask(taskId)); + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 67e99d73b811..5e6b3d559303 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -30,6 +30,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; +import android.graphics.Region; import android.graphics.drawable.Drawable; import android.os.Handler; import android.util.Log; @@ -80,6 +81,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration mResult = new WindowDecoration.RelayoutResult<>(); + private final Point mPositionInParent = new Point(); private final PointF mHandleMenuAppInfoPillPosition = new PointF(); private final PointF mHandleMenuWindowingPillPosition = new PointF(); private final PointF mHandleMenuMoreActionsPillPosition = new PointF(); @@ -92,6 +94,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration