summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Matt Sziklay <mattsziklay@google.com> 2022-12-14 17:41:42 +0000
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2022-12-14 17:41:42 +0000
commit94e8d0ee8020bb6fa22e3f60f754eef7dbe041c8 (patch)
tree5d99169ffe81e0f9a0e259def9e7564276ab867a
parent0f0b247bdf152323105613ce97996716f8ea557b (diff)
parentf5ac31a93f19976579524974e16f79c1e6fca266 (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>
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java42
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java209
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;
- }
- }
}