Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +19 −23 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading @@ -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, Loading @@ -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, Loading @@ -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; Loading @@ -125,11 +124,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mDesktopModeController = desktopModeController; mCaptionWindowDecorFactory = captionWindowDecorFactory; mInputManagerSupplier = inputManagerSupplier; } void setEventReceiverFactory(EventReceiverFactory eventReceiverFactory) { mEventReceiverFactory = eventReceiverFactory; mInputMonitorFactory = inputMonitorFactory; } @Override Loading Loading @@ -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); } } Loading Loading @@ -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 * Loading Loading @@ -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); } Loading Loading @@ -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()); } } } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java +119 −90 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -104,30 +96,36 @@ 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); runOnMainThread(() -> { 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); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED); taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT); }); verify(mCaptionWindowDecorFactory) .create( mContext, Loading @@ -138,10 +136,6 @@ public class CaptionWindowDecorViewModelTests extends ShellTestCase { 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(); } Loading @@ -149,31 +143,21 @@ 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); 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); 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) }); verify(mCaptionWindowDecorFactory, times(1)) .create( mContext, mDisplayController, Loading @@ -185,34 +169,79 @@ public class CaptionWindowDecorViewModelTests extends ShellTestCase { mSyncQueue); } private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int windowingMode) { @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.onTaskOpening(taskInfo, surfaceControl, startT, finishT); mCaptionWindowDecorViewModel.destroyWindowDecoration(taskInfo); }); verify(mMockInputMonitorFactory).create(any(), any()); verify(mInputMonitor).dispose(); } @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; } } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +19 −23 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading @@ -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, Loading @@ -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, Loading @@ -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; Loading @@ -125,11 +124,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mDesktopModeController = desktopModeController; mCaptionWindowDecorFactory = captionWindowDecorFactory; mInputManagerSupplier = inputManagerSupplier; } void setEventReceiverFactory(EventReceiverFactory eventReceiverFactory) { mEventReceiverFactory = eventReceiverFactory; mInputMonitorFactory = inputMonitorFactory; } @Override Loading Loading @@ -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); } } Loading Loading @@ -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 * Loading Loading @@ -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); } Loading Loading @@ -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()); } } }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java +119 −90 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -104,30 +96,36 @@ 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); runOnMainThread(() -> { 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); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED); taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT); }); verify(mCaptionWindowDecorFactory) .create( mContext, Loading @@ -138,10 +136,6 @@ public class CaptionWindowDecorViewModelTests extends ShellTestCase { 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(); } Loading @@ -149,31 +143,21 @@ 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); 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); 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) }); verify(mCaptionWindowDecorFactory, times(1)) .create( mContext, mDisplayController, Loading @@ -185,34 +169,79 @@ public class CaptionWindowDecorViewModelTests extends ShellTestCase { mSyncQueue); } private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int windowingMode) { @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.onTaskOpening(taskInfo, surfaceControl, startT, finishT); mCaptionWindowDecorViewModel.destroyWindowDecoration(taskInfo); }); verify(mMockInputMonitorFactory).create(any(), any()); verify(mInputMonitor).dispose(); } @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; } } }