diff options
| author | 2022-12-14 17:41:42 +0000 | |
|---|---|---|
| committer | 2022-12-14 17:41:42 +0000 | |
| commit | 94e8d0ee8020bb6fa22e3f60f754eef7dbe041c8 (patch) | |
| tree | 5d99169ffe81e0f9a0e259def9e7564276ab867a | |
| parent | 0f0b247bdf152323105613ce97996716f8ea557b (diff) | |
| parent | f5ac31a93f19976579524974e16f79c1e6fca266 (diff) | |
Merge "Add unit test for EventReceiver class in CaptionWindowDecorViewModel" into tm-qpr-dev am: 544e44f249 am: f5ac31a93f
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20693841
Change-Id: Ie1e9a798daaedb527bde29cbff2824ae1117eecd
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
2 files changed, 138 insertions, 113 deletions
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 afefd5dc6344..42e2b3fadf19 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 @@ -46,6 +46,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; @@ -56,7 +57,6 @@ import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.transition.Transitions; import java.util.Optional; -import java.util.function.Supplier; /** * View model for the window decoration with a caption and shadows. Works with @@ -66,7 +66,6 @@ import java.util.function.Supplier; public class CaptionWindowDecorViewModel implements WindowDecorViewModel { private static final String TAG = "CaptionViewModel"; private final CaptionWindowDecoration.Factory mCaptionWindowDecorFactory; - private final Supplier<InputManager> mInputManagerSupplier; private final ActivityTaskManager mActivityTaskManager; private final ShellTaskOrganizer mTaskOrganizer; private final Context mContext; @@ -82,7 +81,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>(); private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl(); - private EventReceiverFactory mEventReceiverFactory = new EventReceiverFactory(); + private InputMonitorFactory mInputMonitorFactory; public CaptionWindowDecorViewModel( Context context, @@ -101,10 +100,11 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { syncQueue, desktopModeController, new CaptionWindowDecoration.Factory(), - InputManager::getInstance); + new InputMonitorFactory()); } - public CaptionWindowDecorViewModel( + @VisibleForTesting + CaptionWindowDecorViewModel( Context context, Handler mainHandler, Choreographer mainChoreographer, @@ -113,8 +113,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { SyncTransactionQueue syncQueue, Optional<DesktopModeController> desktopModeController, CaptionWindowDecoration.Factory captionWindowDecorFactory, - Supplier<InputManager> inputManagerSupplier) { - + InputMonitorFactory inputMonitorFactory) { mContext = context; mMainHandler = mainHandler; mMainChoreographer = mainChoreographer; @@ -125,11 +124,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mDesktopModeController = desktopModeController; mCaptionWindowDecorFactory = captionWindowDecorFactory; - mInputManagerSupplier = inputManagerSupplier; - } - - void setEventReceiverFactory(EventReceiverFactory eventReceiverFactory) { - mEventReceiverFactory = eventReceiverFactory; + mInputMonitorFactory = inputMonitorFactory; } @Override @@ -205,7 +200,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { decoration.close(); int displayId = taskInfo.displayId; if (mEventReceiversByDisplay.contains(displayId)) { - EventReceiver eventReceiver = mEventReceiversByDisplay.get(displayId); removeTaskFromEventReceiver(displayId); } } @@ -408,12 +402,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { } } - class EventReceiverFactory { - EventReceiver create(InputMonitor inputMonitor, InputChannel channel, Looper looper) { - return new EventReceiver(inputMonitor, channel, looper); - } - } - /** * Handle MotionEvents relevant to focused task's caption that don't directly touch it * @@ -500,11 +488,11 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { } private void createInputChannel(int displayId) { - InputManager inputManager = mInputManagerSupplier.get(); + InputManager inputManager = InputManager.getInstance(); InputMonitor inputMonitor = - inputManager.monitorGestureInput("caption-touch", mContext.getDisplayId()); - EventReceiver eventReceiver = mEventReceiverFactory.create( - inputMonitor, inputMonitor.getInputChannel(), Looper.myLooper()); + mInputMonitorFactory.create(inputManager, mContext); + EventReceiver eventReceiver = new EventReceiver(inputMonitor, + inputMonitor.getInputChannel(), Looper.myLooper()); mEventReceiversByDisplay.put(displayId, eventReceiver); } @@ -562,4 +550,12 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mWindowDecorByTaskId.get(taskId).closeHandleMenu(); } } + + static class InputMonitorFactory { + InputMonitor create(InputManager inputManager, Context context) { + return inputManager.monitorGestureInput("caption-touch", context.getDisplayId()); + } + } } + + diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java index ad6fcedd3166..0dbf30d69f75 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java @@ -21,14 +21,15 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; +import android.hardware.display.DisplayManager; +import android.hardware.display.VirtualDisplay; import android.hardware.input.InputManager; import android.os.Handler; import android.os.Looper; @@ -37,9 +38,9 @@ import android.view.Display; import android.view.InputChannel; import android.view.InputMonitor; import android.view.SurfaceControl; +import android.view.SurfaceView; import androidx.test.filters.SmallTest; -import androidx.test.rule.GrantPermissionRule; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; @@ -55,37 +56,28 @@ import org.mockito.Mock; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.function.Supplier; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** Tests of {@link CaptionWindowDecorViewModel} */ @SmallTest public class CaptionWindowDecorViewModelTests extends ShellTestCase { - @Mock private CaptionWindowDecoration mCaptionWindowDecoration; + private static final String TAG = "CaptionWindowDecorViewModelTests"; + + @Mock private CaptionWindowDecoration mCaptionWindowDecoration; @Mock private CaptionWindowDecoration.Factory mCaptionWindowDecorFactory; @Mock private Handler mMainHandler; - @Mock private Choreographer mMainChoreographer; - @Mock private ShellTaskOrganizer mTaskOrganizer; - @Mock private DisplayController mDisplayController; - @Mock private SyncTransactionQueue mSyncQueue; - @Mock private DesktopModeController mDesktopModeController; - @Mock private InputMonitor mInputMonitor; - - @Mock private InputChannel mInputChannel; - - @Mock private CaptionWindowDecorViewModel.EventReceiverFactory mEventReceiverFactory; - - @Mock private CaptionWindowDecorViewModel.EventReceiver mEventReceiver; - @Mock private InputManager mInputManager; + @Mock private CaptionWindowDecorViewModel.InputMonitorFactory mMockInputMonitorFactory; private final List<InputManager> mMockInputManagers = new ArrayList<>(); private CaptionWindowDecorViewModel mCaptionWindowDecorViewModel; @@ -104,44 +96,46 @@ public class CaptionWindowDecorViewModelTests extends ShellTestCase { mSyncQueue, Optional.of(mDesktopModeController), mCaptionWindowDecorFactory, - new MockObjectSupplier<>(mMockInputManagers, () -> mock(InputManager.class))); - mCaptionWindowDecorViewModel.setEventReceiverFactory(mEventReceiverFactory); + mMockInputMonitorFactory + ); 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(), any())).thenReturn(mEventReceiver); - when(mInputMonitor.getInputChannel()).thenReturn(mInputChannel); + when(mMockInputMonitorFactory.create(any(), any())).thenReturn(mInputMonitor); + // InputChannel cannot be mocked because it passes to InputEventReceiver. + final InputChannel[] inputChannels = InputChannel.openInputChannelPair(TAG); + inputChannels[0].dispose(); + when(mInputMonitor.getInputChannel()).thenReturn(inputChannels[1]); } @Test public void testDeleteCaptionOnChangeTransitionWhenNecessary() throws Exception { - Looper.prepare(); final int taskId = 1; final ActivityManager.RunningTaskInfo taskInfo = - createTaskInfo(taskId, WINDOWING_MODE_FREEFORM); + createTaskInfo(taskId, Display.DEFAULT_DISPLAY, 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); + runOnMainThread(() -> { + final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); + final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); + + mCaptionWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT, finishT); - mCaptionWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT, finishT); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED); + taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); + mCaptionWindowDecorViewModel.onTaskChanging(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); + mContext, + mDisplayController, + mTaskOrganizer, + taskInfo, + surfaceControl, + mMainHandler, + mMainChoreographer, + mSyncQueue); verify(mCaptionWindowDecoration).close(); } @@ -149,70 +143,105 @@ public class CaptionWindowDecorViewModelTests extends ShellTestCase { public void testCreateCaptionOnChangeTransitionWhenNecessary() throws Exception { final int taskId = 1; final ActivityManager.RunningTaskInfo taskInfo = - createTaskInfo(taskId, WINDOWING_MODE_UNDEFINED); + createTaskInfo(taskId, Display.DEFAULT_DISPLAY, 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); + runOnMainThread(() -> { + 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); - mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD); - verify(mCaptionWindowDecorFactory, never()) + mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT); + }); + verify(mCaptionWindowDecorFactory, times(1)) .create( - mContext, - mDisplayController, - mTaskOrganizer, - taskInfo, - surfaceControl, - mMainHandler, - mMainChoreographer, - mSyncQueue); - - taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + mContext, + mDisplayController, + mTaskOrganizer, + taskInfo, + surfaceControl, + mMainHandler, + mMainChoreographer, + mSyncQueue); + } + + @Test + public void testCreateAndDisposeEventReceiver() throws Exception { + final int taskId = 1; + final ActivityManager.RunningTaskInfo taskInfo = + createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM); taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD); + runOnMainThread(() -> { + SurfaceControl surfaceControl = mock(SurfaceControl.class); + final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); + final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); - mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT); + mCaptionWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT, finishT); - verify(mCaptionWindowDecorFactory) - .create( - mContext, - mDisplayController, - mTaskOrganizer, - taskInfo, - surfaceControl, - mMainHandler, - mMainChoreographer, - mSyncQueue); + mCaptionWindowDecorViewModel.destroyWindowDecoration(taskInfo); + }); + verify(mMockInputMonitorFactory).create(any(), any()); + verify(mInputMonitor).dispose(); } - private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int windowingMode) { + @Test + public void testEventReceiversOnMultipleDisplays() throws Exception { + runOnMainThread(() -> { + SurfaceView surfaceView = new SurfaceView(mContext); + final DisplayManager mDm = mContext.getSystemService(DisplayManager.class); + final VirtualDisplay secondaryDisplay = mDm.createVirtualDisplay( + "testEventReceiversOnMultipleDisplays", /*width=*/ 400, /*height=*/ 400, + /*densityDpi=*/ 320, surfaceView.getHolder().getSurface(), + DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY); + int secondaryDisplayId = secondaryDisplay.getDisplay().getDisplayId(); + + final int taskId = 1; + final ActivityManager.RunningTaskInfo taskInfo = + createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM); + final ActivityManager.RunningTaskInfo secondTaskInfo = + createTaskInfo(taskId + 1, secondaryDisplayId, WINDOWING_MODE_FREEFORM); + final ActivityManager.RunningTaskInfo thirdTaskInfo = + createTaskInfo(taskId + 2, secondaryDisplayId, WINDOWING_MODE_FREEFORM); + + SurfaceControl surfaceControl = mock(SurfaceControl.class); + final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); + final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); + + mCaptionWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT, finishT); + mCaptionWindowDecorViewModel.onTaskOpening(secondTaskInfo, surfaceControl, + startT, finishT); + mCaptionWindowDecorViewModel.onTaskOpening(thirdTaskInfo, surfaceControl, + startT, finishT); + mCaptionWindowDecorViewModel.destroyWindowDecoration(thirdTaskInfo); + mCaptionWindowDecorViewModel.destroyWindowDecoration(taskInfo); + }); + verify(mMockInputMonitorFactory, times(2)).create(any(), any()); + verify(mInputMonitor, times(1)).dispose(); + } + + private void runOnMainThread(Runnable r) throws Exception { + final Handler mainHandler = new Handler(Looper.getMainLooper()); + final CountDownLatch latch = new CountDownLatch(1); + mainHandler.post(() -> { + r.run(); + latch.countDown(); + }); + latch.await(20, TimeUnit.MILLISECONDS); + } + + private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, + int displayId, int windowingMode) { ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() - .setDisplayId(Display.DEFAULT_DISPLAY) + .setDisplayId(displayId) .setVisible(true) .build(); taskInfo.taskId = taskId; taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode); return taskInfo; } - - private static class MockObjectSupplier<T> implements Supplier<T> { - private final List<T> mObjects; - private final Supplier<T> mDefaultSupplier; - private int mNumOfCalls = 0; - - private MockObjectSupplier(List<T> objects, Supplier<T> defaultSupplier) { - mObjects = objects; - mDefaultSupplier = defaultSupplier; - } - - @Override - public T get() { - final T mock = - mNumOfCalls < mObjects.size() ? mObjects.get(mNumOfCalls) - : mDefaultSupplier.get(); - ++mNumOfCalls; - return mock; - } - } } |