Merge "Open correct handle menu when clicked in split screen." into tm-qpr-dev

This commit is contained in:
Matt Sziklay
2023-02-27 18:28:41 +00:00
committed by Android (Google) Code Review
5 changed files with 97 additions and 26 deletions

View File

@@ -194,7 +194,8 @@ public abstract class WMShellModule {
DisplayController displayController, DisplayController displayController,
SyncTransactionQueue syncQueue, SyncTransactionQueue syncQueue,
Optional<DesktopModeController> desktopModeController, Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController) { Optional<DesktopTasksController> desktopTasksController,
Optional<SplitScreenController> splitScreenController) {
if (DesktopModeStatus.isAnyEnabled()) { if (DesktopModeStatus.isAnyEnabled()) {
return new DesktopModeWindowDecorViewModel( return new DesktopModeWindowDecorViewModel(
context, context,
@@ -204,7 +205,8 @@ public abstract class WMShellModule {
displayController, displayController,
syncQueue, syncQueue,
desktopModeController, desktopModeController,
desktopTasksController); desktopTasksController,
splitScreenController);
} }
return new CaptionWindowDecorViewModel( return new CaptionWindowDecorViewModel(
context, context,

View File

@@ -422,6 +422,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator.goToFullscreenFromSplit(); mStageCoordinator.goToFullscreenFromSplit();
} }
/** Move the specified task to fullscreen, regardless of focus state. */
public void moveTaskToFullscreen(int taskId) {
mStageCoordinator.moveTaskToFullscreen(taskId);
}
public boolean isLaunchToSplit(TaskInfo taskInfo) { public boolean isLaunchToSplit(TaskInfo taskInfo) {
return mStageCoordinator.isLaunchToSplit(taskInfo); return mStageCoordinator.isLaunchToSplit(taskInfo);
} }

View File

@@ -2390,6 +2390,20 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT); mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT);
} }
/** Move the specified task to fullscreen, regardless of focus state. */
public void moveTaskToFullscreen(int taskId) {
boolean leftOrTop;
if (mMainStage.containsTask(taskId)) {
leftOrTop = (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
} else if (mSideStage.containsTask(taskId)) {
leftOrTop = (mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT);
} else {
return;
}
mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT);
}
boolean isLaunchToSplit(TaskInfo taskInfo) { boolean isLaunchToSplit(TaskInfo taskInfo) {
return getActivateSplitPosition(taskInfo) != SPLIT_POSITION_UNDEFINED; return getActivateSplitPosition(taskInfo) != SPLIT_POSITION_UNDEFINED;
} }

View File

@@ -20,10 +20,14 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager; import android.app.ActivityTaskManager;
import android.content.Context; import android.content.Context;
import android.graphics.Rect;
import android.hardware.input.InputManager; import android.hardware.input.InputManager;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@@ -37,7 +41,6 @@ import android.view.MotionEvent;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import android.view.View; import android.view.View;
import android.window.WindowContainerToken; import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -50,6 +53,7 @@ import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.util.Optional; import java.util.Optional;
@@ -80,6 +84,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private final InputMonitorFactory mInputMonitorFactory; private final InputMonitorFactory mInputMonitorFactory;
private TaskOperations mTaskOperations; private TaskOperations mTaskOperations;
private Optional<SplitScreenController> mSplitScreenController;
public DesktopModeWindowDecorViewModel( public DesktopModeWindowDecorViewModel(
Context context, Context context,
Handler mainHandler, Handler mainHandler,
@@ -88,7 +94,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
DisplayController displayController, DisplayController displayController,
SyncTransactionQueue syncQueue, SyncTransactionQueue syncQueue,
Optional<DesktopModeController> desktopModeController, Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController) { Optional<DesktopTasksController> desktopTasksController,
Optional<SplitScreenController> splitScreenController) {
this( this(
context, context,
mainHandler, mainHandler,
@@ -98,6 +105,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
syncQueue, syncQueue,
desktopModeController, desktopModeController,
desktopTasksController, desktopTasksController,
splitScreenController,
new DesktopModeWindowDecoration.Factory(), new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory()); new InputMonitorFactory());
} }
@@ -112,6 +120,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
SyncTransactionQueue syncQueue, SyncTransactionQueue syncQueue,
Optional<DesktopModeController> desktopModeController, Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController, Optional<DesktopTasksController> desktopTasksController,
Optional<SplitScreenController> splitScreenController,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory) { InputMonitorFactory inputMonitorFactory) {
mContext = context; mContext = context;
@@ -120,6 +129,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class); mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
mTaskOrganizer = taskOrganizer; mTaskOrganizer = taskOrganizer;
mDisplayController = displayController; mDisplayController = displayController;
mSplitScreenController = splitScreenController;
mSyncQueue = syncQueue; mSyncQueue = syncQueue;
mDesktopModeController = desktopModeController; mDesktopModeController = desktopModeController;
mDesktopTasksController = desktopTasksController; mDesktopTasksController = desktopTasksController;
@@ -230,6 +240,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final int id = v.getId(); final int id = v.getId();
if (id == R.id.close_window || id == R.id.close_button) { if (id == R.id.close_window || id == R.id.close_button) {
mTaskOperations.closeTask(mTaskToken); mTaskOperations.closeTask(mTaskToken);
if (mSplitScreenController.isPresent()
&& mSplitScreenController.get().isSplitScreenVisible()) {
int remainingTaskPosition = mTaskId == mSplitScreenController.get()
.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT).taskId
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
ActivityManager.RunningTaskInfo remainingTask = mSplitScreenController.get()
.getTaskInfo(remainingTaskPosition);
mSplitScreenController.get().moveTaskToFullscreen(remainingTask.taskId);
}
} else if (id == R.id.back_button) { } else if (id == R.id.back_button) {
mTaskOperations.injectBackKey(); mTaskOperations.injectBackKey();
} else if (id == R.id.caption_handle) { } else if (id == R.id.caption_handle) {
@@ -261,9 +280,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
if (taskInfo.isFocused) { if (taskInfo.isFocused) {
return mDragDetector.isDragEvent(); return mDragDetector.isDragEvent();
} }
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reorder(mTaskToken, true /* onTop */);
mSyncQueue.queue(wct);
return false; return false;
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_CANCEL:
@@ -401,14 +417,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
* @param ev the {@link MotionEvent} received by {@link EventReceiver} * @param ev the {@link MotionEvent} received by {@link EventReceiver}
*/ */
private void handleReceivedMotionEvent(MotionEvent ev, InputMonitor inputMonitor) { private void handleReceivedMotionEvent(MotionEvent ev, InputMonitor inputMonitor) {
final DesktopModeWindowDecoration relevantDecor = getRelevantWindowDecor(ev);
if (DesktopModeStatus.isProto2Enabled()) { if (DesktopModeStatus.isProto2Enabled()) {
final DesktopModeWindowDecoration focusedDecor = getFocusedDecor(); if (relevantDecor == null
if (focusedDecor == null || relevantDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
|| focusedDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) { handleCaptionThroughStatusBar(ev, relevantDecor);
handleCaptionThroughStatusBar(ev);
} }
} }
handleEventOutsideFocusedCaption(ev); handleEventOutsideFocusedCaption(ev, relevantDecor);
// Prevent status bar from reacting to a caption drag. // Prevent status bar from reacting to a caption drag.
if (DesktopModeStatus.isProto2Enabled()) { if (DesktopModeStatus.isProto2Enabled()) {
if (mTransitionDragActive) { if (mTransitionDragActive) {
@@ -422,16 +438,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
} }
// If an UP/CANCEL action is received outside of caption bounds, turn off handle menu // If an UP/CANCEL action is received outside of caption bounds, turn off handle menu
private void handleEventOutsideFocusedCaption(MotionEvent ev) { private void handleEventOutsideFocusedCaption(MotionEvent ev,
DesktopModeWindowDecoration relevantDecor) {
final int action = ev.getActionMasked(); final int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
final DesktopModeWindowDecoration focusedDecor = getFocusedDecor(); if (relevantDecor == null) {
if (focusedDecor == null) {
return; return;
} }
if (!mTransitionDragActive) { if (!mTransitionDragActive) {
focusedDecor.closeHandleMenuIfNeeded(ev); relevantDecor.closeHandleMenuIfNeeded(ev);
} }
} }
} }
@@ -441,39 +457,38 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
* Perform caption actions if not able to through normal means. * Perform caption actions if not able to through normal means.
* Turn on desktop mode if handle is dragged below status bar. * Turn on desktop mode if handle is dragged below status bar.
*/ */
private void handleCaptionThroughStatusBar(MotionEvent ev) { private void handleCaptionThroughStatusBar(MotionEvent ev,
DesktopModeWindowDecoration relevantDecor) {
switch (ev.getActionMasked()) { switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: { case MotionEvent.ACTION_DOWN: {
// Begin drag through status bar if applicable. // Begin drag through status bar if applicable.
final DesktopModeWindowDecoration focusedDecor = getFocusedDecor(); if (relevantDecor != null) {
if (focusedDecor != null) {
boolean dragFromStatusBarAllowed = false; boolean dragFromStatusBarAllowed = false;
if (DesktopModeStatus.isProto2Enabled()) { if (DesktopModeStatus.isProto2Enabled()) {
// In proto2 any full screen task can be dragged to freeform // In proto2 any full screen task can be dragged to freeform
dragFromStatusBarAllowed = focusedDecor.mTaskInfo.getWindowingMode() dragFromStatusBarAllowed = relevantDecor.mTaskInfo.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN; == WINDOWING_MODE_FULLSCREEN;
} }
if (dragFromStatusBarAllowed && focusedDecor.checkTouchEventInHandle(ev)) { if (dragFromStatusBarAllowed && relevantDecor.checkTouchEventInHandle(ev)) {
mTransitionDragActive = true; mTransitionDragActive = true;
} }
} }
break; break;
} }
case MotionEvent.ACTION_UP: { case MotionEvent.ACTION_UP: {
final DesktopModeWindowDecoration focusedDecor = getFocusedDecor(); if (relevantDecor == null) {
if (focusedDecor == null) {
mTransitionDragActive = false; mTransitionDragActive = false;
return; return;
} }
if (mTransitionDragActive) { if (mTransitionDragActive) {
mTransitionDragActive = false; mTransitionDragActive = false;
final int statusBarHeight = mDisplayController final int statusBarHeight = mDisplayController
.getDisplayLayout(focusedDecor.mTaskInfo.displayId).stableInsets().top; .getDisplayLayout(relevantDecor.mTaskInfo.displayId).stableInsets().top;
if (ev.getY() > statusBarHeight) { if (ev.getY() > statusBarHeight) {
if (DesktopModeStatus.isProto2Enabled()) { if (DesktopModeStatus.isProto2Enabled()) {
mDesktopTasksController.ifPresent( mDesktopTasksController.ifPresent(
c -> c.moveToDesktop(focusedDecor.mTaskInfo)); c -> c.moveToDesktop(relevantDecor.mTaskInfo));
} else if (DesktopModeStatus.isProto1Enabled()) { } else if (DesktopModeStatus.isProto1Enabled()) {
mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
} }
@@ -481,7 +496,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return; return;
} }
} }
focusedDecor.checkClickEvent(ev); relevantDecor.checkClickEvent(ev);
break; break;
} }
case MotionEvent.ACTION_CANCEL: { case MotionEvent.ACTION_CANCEL: {
@@ -490,6 +505,38 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
} }
} }
@Nullable
private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) {
if (mSplitScreenController.isPresent()
&& mSplitScreenController.get().isSplitScreenVisible()) {
// We can't look at focused task here as only one task will have focus.
return getSplitScreenDecor(ev);
} else {
return getFocusedDecor();
}
}
@Nullable
private DesktopModeWindowDecoration getSplitScreenDecor(MotionEvent ev) {
ActivityManager.RunningTaskInfo topOrLeftTask =
mSplitScreenController.get().getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
ActivityManager.RunningTaskInfo bottomOrRightTask =
mSplitScreenController.get().getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
if (topOrLeftTask != null && topOrLeftTask.getConfiguration()
.windowConfiguration.getBounds().contains((int) ev.getX(), (int) ev.getY())) {
return mWindowDecorByTaskId.get(topOrLeftTask.taskId);
} else if (bottomOrRightTask != null && bottomOrRightTask.getConfiguration()
.windowConfiguration.getBounds().contains((int) ev.getX(), (int) ev.getY())) {
Rect bottomOrRightBounds = bottomOrRightTask.getConfiguration().windowConfiguration
.getBounds();
ev.offsetLocation(-bottomOrRightBounds.left, -bottomOrRightBounds.top);
return mWindowDecorByTaskId.get(bottomOrRightTask.taskId);
} else {
return null;
}
}
@Nullable @Nullable
private DesktopModeWindowDecoration getFocusedDecor() { private DesktopModeWindowDecoration getFocusedDecor() {
final int size = mWindowDecorByTaskId.size(); final int size = mWindowDecorByTaskId.size();

View File

@@ -49,6 +49,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -73,6 +74,7 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase {
@Mock private Choreographer mMainChoreographer; @Mock private Choreographer mMainChoreographer;
@Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private DisplayController mDisplayController; @Mock private DisplayController mDisplayController;
@Mock private SplitScreenController mSplitScreenController;
@Mock private SyncTransactionQueue mSyncQueue; @Mock private SyncTransactionQueue mSyncQueue;
@Mock private DesktopModeController mDesktopModeController; @Mock private DesktopModeController mDesktopModeController;
@Mock private DesktopTasksController mDesktopTasksController; @Mock private DesktopTasksController mDesktopTasksController;
@@ -98,6 +100,7 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase {
mSyncQueue, mSyncQueue,
Optional.of(mDesktopModeController), Optional.of(mDesktopModeController),
Optional.of(mDesktopTasksController), Optional.of(mDesktopTasksController),
Optional.of(mSplitScreenController),
mDesktopModeWindowDecorFactory, mDesktopModeWindowDecorFactory,
mMockInputMonitorFactory mMockInputMonitorFactory
); );