From b977f682d8d5780860bee834c6d25c47a416d199 Mon Sep 17 00:00:00 2001 From: maryam Date: Tue, 22 Nov 2022 00:09:09 -0800 Subject: [PATCH] Rename WindowDecorModelView to reflect purposes Changed method names in WindowDecorModel Interface to better reflect their purposes and made onTaskChanging (Previously setupWindowDecorationForTransition) to handle captionCreation if needed. Test: Manual Testing and created CaptionWindowDecorViewModelTests Bug: 257288823 Change-Id: I43672fa37accba97b4ea330e01082d00137a4b01 --- .../shell/freeform/FreeformTaskListener.java | 2 +- .../FreeformTaskTransitionObserver.java | 22 +- .../fullscreen/FullscreenTaskListener.java | 2 +- .../CaptionWindowDecorViewModel.java | 156 +++++++++---- .../windowdecor/CaptionWindowDecoration.java | 45 +++- .../windowdecor/WindowDecorViewModel.java | 46 ++-- .../FreeformTaskTransitionObserverTest.java | 4 +- .../CaptionWindowDecorViewModelTests.java | 217 ++++++++++++++++++ 8 files changed, 408 insertions(+), 86 deletions(-) create mode 100644 libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java index 90b35a5a55e1d..44bcdb2d5de51 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java @@ -82,7 +82,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener { mTasks.put(taskInfo.taskId, state); if (!Transitions.ENABLE_SHELL_TRANSITIONS) { SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t); + mWindowDecorationViewModel.onTaskOpening(taskInfo, leash, t, t); t.apply(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java index 168f6d79a3901..6e710f7caedae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java @@ -120,7 +120,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs TransitionInfo.Change change, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { - mWindowDecorViewModel.createWindowDecoration( + mWindowDecorViewModel.onTaskOpening( change.getTaskInfo(), change.getLeash(), startT, finishT); } @@ -128,31 +128,23 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs TransitionInfo.Change change, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { - mWindowDecorViewModel.setupWindowDecorationForTransition( - change.getTaskInfo(), startT, finishT); + mWindowDecorViewModel.onTaskClosing(change.getTaskInfo(), startT, finishT); } private void onChangeTransitionReady( TransitionInfo.Change change, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { - mWindowDecorViewModel.setupWindowDecorationForTransition( - change.getTaskInfo(), startT, finishT); + mWindowDecorViewModel.onTaskChanging( + change.getTaskInfo(), change.getLeash(), startT, finishT); } private void onToFrontTransitionReady( TransitionInfo.Change change, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { - boolean exists = mWindowDecorViewModel.setupWindowDecorationForTransition( - change.getTaskInfo(), - startT, - finishT); - if (!exists) { - // Window caption does not exist, create it - mWindowDecorViewModel.createWindowDecoration( - change.getTaskInfo(), change.getLeash(), startT, finishT); - } + mWindowDecorViewModel.onTaskChanging( + change.getTaskInfo(), change.getLeash(), startT, finishT); } @Override @@ -188,4 +180,4 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs mWindowDecorViewModel.destroyWindowDecoration(taskInfo.get(i)); } } -} +} \ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java index 75a4091c7d782..6623f5ca84eef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java @@ -103,7 +103,7 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { if (mWindowDecorViewModelOptional.isPresent()) { SurfaceControl.Transaction t = new SurfaceControl.Transaction(); createdWindowDecor = mWindowDecorViewModelOptional.get() - .createWindowDecoration(taskInfo, leash, t, t); + .onTaskOpening(taskInfo, leash, t, t); t.apply(); } if (!createdWindowDecor) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index 8369569b41633..e40db4e4dcf24 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -55,6 +55,8 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.transition.Transitions; +import java.util.function.Supplier; + /** * View model for the window decoration with a caption and shadows. Works with * {@link CaptionWindowDecoration}. @@ -62,6 +64,8 @@ import com.android.wm.shell.transition.Transitions; public class CaptionWindowDecorViewModel implements WindowDecorViewModel { private static final String TAG = "CaptionViewModel"; + private final CaptionWindowDecoration.Factory mCaptionWindowDecorFactory; + private final Supplier mInputManagerSupplier; private final ActivityTaskManager mActivityTaskManager; private final ShellTaskOrganizer mTaskOrganizer; private final Context mContext; @@ -77,6 +81,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { private final SparseArray mWindowDecorByTaskId = new SparseArray<>(); private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl(); + private EventReceiverFactory mEventReceiverFactory = new EventReceiverFactory(); public CaptionWindowDecorViewModel( Context context, @@ -86,6 +91,29 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { DisplayController displayController, SyncTransactionQueue syncQueue, DesktopModeController desktopModeController) { + this( + context, + mainHandler, + mainChoreographer, + taskOrganizer, + displayController, + syncQueue, + desktopModeController, + new CaptionWindowDecoration.Factory(), + InputManager::getInstance); + } + + public CaptionWindowDecorViewModel( + Context context, + Handler mainHandler, + Choreographer mainChoreographer, + ShellTaskOrganizer taskOrganizer, + DisplayController displayController, + SyncTransactionQueue syncQueue, + DesktopModeController desktopModeController, + CaptionWindowDecoration.Factory captionWindowDecorFactory, + Supplier inputManagerSupplier) { + mContext = context; mMainHandler = mainHandler; mMainChoreographer = mainChoreographer; @@ -94,7 +122,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mDisplayController = displayController; mSyncQueue = syncQueue; mDesktopModeController = desktopModeController; - mTransitionDragActive = false; + + mCaptionWindowDecorFactory = captionWindowDecorFactory; + mInputManagerSupplier = inputManagerSupplier; + } + + void setEventReceiverFactory(EventReceiverFactory eventReceiverFactory) { + mEventReceiverFactory = eventReceiverFactory; } @Override @@ -103,42 +137,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { } @Override - public boolean createWindowDecoration( + public boolean onTaskOpening( ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { if (!shouldShowWindowDecor(taskInfo)) return false; - CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId); - if (oldDecoration != null) { - // close the old decoration if it exists to avoid two window decorations being added - oldDecoration.close(); - } - final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration( - mContext, - mDisplayController, - mTaskOrganizer, - taskInfo, - taskSurface, - mMainHandler, - mMainChoreographer, - mSyncQueue); - mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); - - TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration, - mDragStartListener); - CaptionTouchEventListener touchEventListener = - new CaptionTouchEventListener(taskInfo, taskPositioner, - windowDecoration.getDragDetector()); - windowDecoration.setCaptionListeners(touchEventListener, touchEventListener); - windowDecoration.setDragResizeCallback(taskPositioner); - setupWindowDecorationForTransition(taskInfo, startT, finishT); - if (mInputMonitor == null) { - mInputMonitor = InputManager.getInstance().monitorGestureInput( - "caption-touch", mContext.getDisplayId()); - mEventReceiver = new EventReceiver( - mInputMonitor.getInputChannel(), Looper.myLooper()); - } + createWindowDecoration(taskInfo, taskSurface, startT, finishT); return true; } @@ -151,25 +156,45 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { } @Override - public boolean setupWindowDecorationForTransition( + public void onTaskChanging( + RunningTaskInfo taskInfo, + SurfaceControl taskSurface, + SurfaceControl.Transaction startT, + SurfaceControl.Transaction finishT) { + final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId); + + if (!shouldShowWindowDecor(taskInfo)) { + if (decoration != null) { + destroyWindowDecoration(taskInfo); + } + return; + } + + if (decoration == null) { + createWindowDecoration(taskInfo, taskSurface, startT, finishT); + } else { + decoration.relayout(taskInfo, startT, finishT); + } + } + + @Override + public void onTaskClosing( RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId); - if (decoration == null) return false; + if (decoration == null) return; decoration.relayout(taskInfo, startT, finishT); - return true; } @Override - public boolean destroyWindowDecoration(RunningTaskInfo taskInfo) { + public void destroyWindowDecoration(RunningTaskInfo taskInfo) { final CaptionWindowDecoration decoration = mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId); - if (decoration == null) return false; + if (decoration == null) return; decoration.close(); - return true; } private class CaptionTouchEventListener implements @@ -217,6 +242,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { decoration.setButtonVisibility(); } } + private void injectBackKey() { sendBackEvent(KeyEvent.ACTION_DOWN); sendBackEvent(KeyEvent.ACTION_UP); @@ -266,7 +292,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { */ private void handleEventForMove(MotionEvent e) { RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId); - int windowingMode = mDesktopModeController + int windowingMode = mDesktopModeController .getDisplayAreaWindowingMode(taskInfo.displayId); if (windowingMode == WINDOWING_MODE_FULLSCREEN) { return; @@ -302,7 +328,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { } // InputEventReceiver to listen for touch input outside of caption bounds - private class EventReceiver extends InputEventReceiver { + class EventReceiver extends InputEventReceiver { EventReceiver(InputChannel channel, Looper looper) { super(channel, looper); } @@ -318,8 +344,15 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { } } + class EventReceiverFactory { + EventReceiver create(InputChannel channel, Looper looper) { + return new EventReceiver(channel, looper); + } + } + /** * Handle MotionEvents relevant to focused task's caption that don't directly touch it + * * @param ev the {@link MotionEvent} received by {@link EventReceiver} */ private void handleReceivedMotionEvent(MotionEvent ev) { @@ -401,7 +434,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { return focusedDecor; } - private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true; return DesktopModeStatus.IS_SUPPORTED @@ -410,7 +442,47 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { .getResources().getConfiguration().smallestScreenWidthDp >= 600; } - private class DragStartListenerImpl implements TaskPositioner.DragStartListener{ + private void createWindowDecoration( + ActivityManager.RunningTaskInfo taskInfo, + SurfaceControl taskSurface, + SurfaceControl.Transaction startT, + SurfaceControl.Transaction finishT) { + CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId); + if (oldDecoration != null) { + // close the old decoration if it exists to avoid two window decorations being added + oldDecoration.close(); + } + final CaptionWindowDecoration windowDecoration = + mCaptionWindowDecorFactory.create( + mContext, + mDisplayController, + mTaskOrganizer, + taskInfo, + taskSurface, + mMainHandler, + mMainChoreographer, + mSyncQueue); + mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); + + TaskPositioner taskPositioner = + new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener); + CaptionTouchEventListener touchEventListener = + new CaptionTouchEventListener( + taskInfo, taskPositioner, windowDecoration.getDragDetector()); + windowDecoration.setCaptionListeners(touchEventListener, touchEventListener); + windowDecoration.setDragResizeCallback(taskPositioner); + windowDecoration.relayout(taskInfo, startT, finishT); + if (mInputMonitor == null) { + InputManager inputManager = mInputManagerSupplier.get(); + mInputMonitor = + inputManager.monitorGestureInput("caption-touch", mContext.getDisplayId()); + mEventReceiver = + mEventReceiverFactory.create( + mInputMonitor.getInputChannel(), Looper.myLooper()); + } + } + + private class DragStartListenerImpl implements TaskPositioner.DragStartListener { @Override public void onDragStart(int taskId) { mWindowDecorByTaskId.get(taskId).closeHandleMenu(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 59576cd3ec15c..037ca2031254f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -42,7 +42,8 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus; /** * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with - * {@link CaptionWindowDecorViewModel}. The caption bar contains a handle, back button, and close button. + * {@link CaptionWindowDecorViewModel}. The caption bar contains a handle, back button, and close + * button. * * The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't. */ @@ -181,12 +182,12 @@ public class CaptionWindowDecoration extends WindowDecoration mMockInputManagers = new ArrayList<>(); + + private CaptionWindowDecorViewModel mCaptionWindowDecorViewModel; + + @Before + public void setUp() { + mMockInputManagers.add(mInputManager); + + mCaptionWindowDecorViewModel = + new CaptionWindowDecorViewModel( + mContext, + mMainHandler, + mMainChoreographer, + mTaskOrganizer, + mDisplayController, + mSyncQueue, + mDesktopModeController, + mCaptionWindowDecorFactory, + new MockObjectSupplier<>(mMockInputManagers, () -> mock(InputManager.class))); + mCaptionWindowDecorViewModel.setEventReceiverFactory(mEventReceiverFactory); + + doReturn(mCaptionWindowDecoration) + .when(mCaptionWindowDecorFactory) + .create(any(), any(), any(), any(), any(), any(), any(), any()); + + when(mInputManager.monitorGestureInput(any(), anyInt())).thenReturn(mInputMonitor); + when(mEventReceiverFactory.create(any(), any())).thenReturn(mEventReceiver); + when(mInputMonitor.getInputChannel()).thenReturn(mInputChannel); + } + + @Test + public void testDeleteCaptionOnChangeTransitionWhenNecessary() throws Exception { + Looper.prepare(); + final int taskId = 1; + final ActivityManager.RunningTaskInfo taskInfo = + createTaskInfo(taskId, WINDOWING_MODE_FREEFORM); + SurfaceControl surfaceControl = mock(SurfaceControl.class); + final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); + final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); + GrantPermissionRule.grant(android.Manifest.permission.MONITOR_INPUT); + + mCaptionWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT, finishT); + verify(mCaptionWindowDecorFactory) + .create( + mContext, + mDisplayController, + mTaskOrganizer, + taskInfo, + surfaceControl, + mMainHandler, + mMainChoreographer, + mSyncQueue); + + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED); + taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); + mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT); + verify(mCaptionWindowDecoration).close(); + } + + @Test + public void testCreateCaptionOnChangeTransitionWhenNecessary() throws Exception { + final int taskId = 1; + final ActivityManager.RunningTaskInfo taskInfo = + createTaskInfo(taskId, WINDOWING_MODE_UNDEFINED); + SurfaceControl surfaceControl = mock(SurfaceControl.class); + final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); + final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); + taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); + + mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT); + + verify(mCaptionWindowDecorFactory, never()) + .create( + mContext, + mDisplayController, + mTaskOrganizer, + taskInfo, + surfaceControl, + mMainHandler, + mMainChoreographer, + mSyncQueue); + + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD); + + mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT); + + verify(mCaptionWindowDecorFactory) + .create( + mContext, + mDisplayController, + mTaskOrganizer, + taskInfo, + surfaceControl, + mMainHandler, + mMainChoreographer, + mSyncQueue); + } + + private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int windowingMode) { + ActivityManager.RunningTaskInfo taskInfo = + new TestRunningTaskInfoBuilder() + .setDisplayId(Display.DEFAULT_DISPLAY) + .setVisible(true) + .build(); + taskInfo.taskId = taskId; + taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode); + return taskInfo; + } + + private static class MockObjectSupplier implements Supplier { + private final List mObjects; + private final Supplier mDefaultSupplier; + private int mNumOfCalls = 0; + + private MockObjectSupplier(List objects, Supplier defaultSupplier) { + mObjects = objects; + mDefaultSupplier = defaultSupplier; + } + + @Override + public T get() { + final T mock = + mNumOfCalls < mObjects.size() ? mObjects.get(mNumOfCalls) + : mDefaultSupplier.get(); + ++mNumOfCalls; + return mock; + } + } +}