| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.accessibility; |
| |
| import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowChangesMatcher.a11yWindowChanges; |
| import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.a11yWindowId; |
| |
| import static junit.framework.Assert.assertFalse; |
| import static junit.framework.Assert.assertNotNull; |
| import static junit.framework.Assert.assertNull; |
| import static junit.framework.Assert.assertTrue; |
| |
| import static org.hamcrest.Matchers.allOf; |
| import static org.hamcrest.Matchers.is; |
| import static org.hamcrest.Matchers.not; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertThat; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.anyString; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.ArgumentMatchers.isNull; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import android.graphics.Region; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| import android.os.UserHandle; |
| import android.util.SparseArray; |
| import android.view.Display; |
| import android.view.IWindow; |
| import android.view.WindowInfo; |
| import android.view.accessibility.AccessibilityEvent; |
| import android.view.accessibility.AccessibilityNodeInfo; |
| import android.view.accessibility.AccessibilityWindowInfo; |
| import android.view.accessibility.IAccessibilityInteractionConnection; |
| |
| import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection; |
| import com.android.server.wm.WindowManagerInternal; |
| import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; |
| |
| import org.hamcrest.Description; |
| import org.hamcrest.TypeSafeMatcher; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Mock; |
| import org.mockito.Mockito; |
| import org.mockito.MockitoAnnotations; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| /** |
| * Tests for the AccessibilityWindowManager |
| */ |
| public class AccessibilityWindowManagerTest { |
| private static final String PACKAGE_NAME = "com.android.server.accessibility"; |
| private static final boolean FORCE_SEND = true; |
| private static final boolean SEND_ON_WINDOW_CHANGES = false; |
| private static final int USER_SYSTEM_ID = UserHandle.USER_SYSTEM; |
| private static final int USER_PROFILE = 11; |
| private static final int USER_PROFILE_PARENT = 1; |
| private static final int SECONDARY_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1; |
| private static final int NUM_GLOBAL_WINDOWS = 4; |
| private static final int NUM_APP_WINDOWS = 4; |
| private static final int NUM_OF_WINDOWS = (NUM_GLOBAL_WINDOWS + NUM_APP_WINDOWS); |
| private static final int DEFAULT_FOCUSED_INDEX = 1; |
| private static final int SCREEN_WIDTH = 1080; |
| private static final int SCREEN_HEIGHT = 1920; |
| |
| private AccessibilityWindowManager mA11yWindowManager; |
| // Window manager will support multiple focused window if config_perDisplayFocusEnabled is true, |
| // i.e., each display would have its current focused window, and one of all focused windows |
| // would be top focused window. Otherwise, window manager only supports one focused window |
| // at all displays, and that focused window would be top focused window. |
| private boolean mSupportPerDisplayFocus = false; |
| private int mTopFocusedDisplayId = Display.INVALID_DISPLAY; |
| private IBinder mTopFocusedWindowToken = null; |
| |
| // List of window token, mapping from windowId -> window token. |
| private final SparseArray<IWindow> mA11yWindowTokens = new SparseArray<>(); |
| // List of window info lists, mapping from displayId -> window info lists. |
| private final SparseArray<ArrayList<WindowInfo>> mWindowInfos = |
| new SparseArray<>(); |
| // List of callback, mapping from displayId -> callback. |
| private final SparseArray<WindowsForAccessibilityCallback> mCallbackOfWindows = |
| new SparseArray<>(); |
| // List of display ID. |
| private final ArrayList<Integer> mExpectedDisplayList = new ArrayList<>(Arrays.asList( |
| Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID)); |
| |
| private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null); |
| |
| @Mock private WindowManagerInternal mMockWindowManagerInternal; |
| @Mock private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender; |
| @Mock private AccessibilitySecurityPolicy mMockA11ySecurityPolicy; |
| @Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager; |
| |
| @Before |
| public void setUp() throws RemoteException { |
| MockitoAnnotations.initMocks(this); |
| when(mMockA11yUserManager.getCurrentUserIdLocked()).thenReturn(USER_SYSTEM_ID); |
| when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked( |
| USER_PROFILE)).thenReturn(USER_PROFILE_PARENT); |
| when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked( |
| USER_SYSTEM_ID)).thenReturn(USER_SYSTEM_ID); |
| when(mMockA11ySecurityPolicy.resolveValidReportedPackageLocked( |
| anyString(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME); |
| |
| mA11yWindowManager = new AccessibilityWindowManager(new Object(), mHandler, |
| mMockWindowManagerInternal, |
| mMockA11yEventSender, |
| mMockA11ySecurityPolicy, |
| mMockA11yUserManager); |
| // Starts tracking window of default display and sets the default display |
| // as top focused display before each testing starts. |
| startTrackingPerDisplay(Display.DEFAULT_DISPLAY); |
| |
| // AccessibilityEventSender is invoked during onWindowsForAccessibilityChanged. |
| // Resets it for mockito verify of further test case. |
| Mockito.reset(mMockA11yEventSender); |
| } |
| |
| @After |
| public void tearDown() { |
| mHandler.removeAllMessages(); |
| } |
| |
| @Test |
| public void startTrackingWindows_shouldEnableWindowManagerCallback() { |
| // AccessibilityWindowManager#startTrackingWindows already invoked in setup. |
| assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); |
| final WindowsForAccessibilityCallback callbacks = |
| mCallbackOfWindows.get(Display.DEFAULT_DISPLAY); |
| verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback( |
| eq(Display.DEFAULT_DISPLAY), eq(callbacks)); |
| } |
| |
| @Test |
| public void stopTrackingWindows_shouldDisableWindowManagerCallback() { |
| assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); |
| Mockito.reset(mMockWindowManagerInternal); |
| |
| mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); |
| assertFalse(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); |
| verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback( |
| eq(Display.DEFAULT_DISPLAY), isNull()); |
| |
| } |
| |
| @Test |
| public void stopTrackingWindows_shouldClearWindows() { |
| assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); |
| final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); |
| |
| mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); |
| assertNull(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)); |
| assertEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), |
| AccessibilityWindowInfo.UNDEFINED_WINDOW_ID); |
| assertEquals(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), |
| activeWindowId); |
| } |
| |
| @Test |
| public void stopTrackingWindows_onNonTopFocusedDisplay_shouldNotResetTopFocusWindow() |
| throws RemoteException { |
| // At setup, the default display sets be the top focused display and |
| // its current focused window sets be the top focused window. |
| // Starts tracking window of second display. |
| startTrackingPerDisplay(SECONDARY_DISPLAY_ID); |
| assertTrue(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID)); |
| // Stops tracking windows of second display. |
| mA11yWindowManager.stopTrackingWindows(SECONDARY_DISPLAY_ID); |
| assertNotEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), |
| AccessibilityWindowInfo.UNDEFINED_WINDOW_ID); |
| } |
| |
| @Test |
| public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() { |
| final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); |
| WindowInfo focusedWindowInfo = |
| mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX); |
| assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked( |
| USER_SYSTEM_ID, focusedWindowInfo.token)); |
| |
| focusedWindowInfo.focused = false; |
| focusedWindowInfo = |
| mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1); |
| focusedWindowInfo.focused = true; |
| |
| mA11yWindowManager.onTouchInteractionStart(); |
| setTopFocusedWindowAndDisplay(Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX + 1); |
| onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); |
| |
| assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); |
| } |
| |
| @Test |
| public void |
| onWindowsChanged_focusChangeOnNonTopFocusedDisplay_perDisplayFocusOn_notChangeWindow() |
| throws RemoteException { |
| // At setup, the default display sets be the top focused display and |
| // its current focused window sets be the top focused window. |
| // Sets supporting multiple focused window, i.e., config_perDisplayFocusEnabled is true. |
| mSupportPerDisplayFocus = true; |
| // Starts tracking window of second display. |
| startTrackingPerDisplay(SECONDARY_DISPLAY_ID); |
| // Gets the active window. |
| final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); |
| // Gets the top focused window. |
| final int topFocusedWindowId = |
| mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT); |
| // Changes the current focused window at second display. |
| changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID, |
| DEFAULT_FOCUSED_INDEX + 1, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX); |
| |
| onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); |
| // The active window should not be changed. |
| assertEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); |
| // The top focused window should not be changed. |
| assertEquals(topFocusedWindowId, |
| mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT)); |
| } |
| |
| @Test |
| public void |
| onWindowChange_focusChangeToNonTopFocusedDisplay_perDisplayFocusOff_shouldChangeWindow() |
| throws RemoteException { |
| // At setup, the default display sets be the top focused display and |
| // its current focused window sets be the top focused window. |
| // Sets not supporting multiple focused window, i.e., config_perDisplayFocusEnabled is |
| // false. |
| mSupportPerDisplayFocus = false; |
| // Starts tracking window of second display. |
| startTrackingPerDisplay(SECONDARY_DISPLAY_ID); |
| // Gets the active window. |
| final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); |
| // Gets the top focused window. |
| final int topFocusedWindowId = |
| mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT); |
| // Changes the current focused window from default display to second display. |
| changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID, |
| DEFAULT_FOCUSED_INDEX, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX); |
| |
| onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); |
| onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); |
| // The active window should be changed. |
| assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); |
| // The top focused window should be changed. |
| assertNotEquals(topFocusedWindowId, |
| mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT)); |
| } |
| |
| @Test |
| public void onWindowsChanged_shouldReportCorrectLayer() { |
| // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup. |
| List<AccessibilityWindowInfo> a11yWindows = |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); |
| for (int i = 0; i < a11yWindows.size(); i++) { |
| final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); |
| final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i); |
| assertThat(mWindowInfos.get(Display.DEFAULT_DISPLAY).size() - windowInfo.layer - 1, |
| is(a11yWindow.getLayer())); |
| } |
| } |
| |
| @Test |
| public void onWindowsChanged_shouldReportCorrectOrder() { |
| // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup. |
| List<AccessibilityWindowInfo> a11yWindows = |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); |
| for (int i = 0; i < a11yWindows.size(); i++) { |
| final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); |
| final IBinder windowToken = mA11yWindowManager |
| .getWindowTokenForUserAndWindowIdLocked(USER_SYSTEM_ID, a11yWindow.getId()); |
| final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i); |
| assertThat(windowToken, is(windowInfo.token)); |
| } |
| } |
| |
| @Test |
| public void onWindowsChangedAndForceSend_shouldUpdateWindows() { |
| final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); |
| final int correctLayer = |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer(); |
| windowInfo.layer += 1; |
| |
| onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); |
| assertNotEquals(correctLayer, |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer()); |
| } |
| |
| @Test |
| public void onWindowsChangedNoForceSend_layerChanged_shouldNotUpdateWindows() { |
| final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); |
| final int correctLayer = |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer(); |
| windowInfo.layer += 1; |
| |
| onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); |
| assertEquals(correctLayer, |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer()); |
| } |
| |
| @Test |
| public void onWindowsChangedNoForceSend_windowChanged_shouldUpdateWindows() |
| throws RemoteException { |
| final AccessibilityWindowInfo oldWindow = |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0); |
| final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, |
| true, USER_SYSTEM_ID); |
| final WindowInfo windowInfo = WindowInfo.obtain(); |
| windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION; |
| windowInfo.token = token.asBinder(); |
| windowInfo.layer = 0; |
| windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); |
| mWindowInfos.get(Display.DEFAULT_DISPLAY).set(0, windowInfo); |
| |
| onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); |
| assertNotEquals(oldWindow, |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0)); |
| } |
| |
| @Test |
| public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() { |
| final WindowInfo focusedWindowInfo = |
| mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX); |
| final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); |
| focusedWindowInfo.focused = false; |
| windowInfo.focused = true; |
| |
| onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); |
| assertTrue(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0) |
| .isFocused()); |
| } |
| |
| @Test |
| public void removeAccessibilityInteractionConnection_byWindowToken_shouldRemoved() { |
| for (int i = 0; i < NUM_OF_WINDOWS; i++) { |
| final int windowId = mA11yWindowTokens.keyAt(i); |
| final IWindow windowToken = mA11yWindowTokens.valueAt(i); |
| assertNotNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId)); |
| |
| mA11yWindowManager.removeAccessibilityInteractionConnection(windowToken); |
| assertNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId)); |
| } |
| } |
| |
| @Test |
| public void remoteAccessibilityConnection_binderDied_shouldRemoveConnection() { |
| for (int i = 0; i < NUM_OF_WINDOWS; i++) { |
| final int windowId = mA11yWindowTokens.keyAt(i); |
| final RemoteAccessibilityConnection remoteA11yConnection = |
| mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId); |
| assertNotNull(remoteA11yConnection); |
| |
| remoteA11yConnection.binderDied(); |
| assertNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId)); |
| } |
| } |
| |
| @Test |
| public void getWindowTokenForUserAndWindowId_shouldNotNull() { |
| final List<AccessibilityWindowInfo> windows = |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); |
| for (int i = 0; i < windows.size(); i++) { |
| final int windowId = windows.get(i).getId(); |
| |
| assertNotNull(mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked( |
| USER_SYSTEM_ID, windowId)); |
| } |
| } |
| |
| @Test |
| public void findWindowId() { |
| final List<AccessibilityWindowInfo> windows = |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); |
| for (int i = 0; i < windows.size(); i++) { |
| final int windowId = windows.get(i).getId(); |
| final IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked( |
| USER_SYSTEM_ID, windowId); |
| |
| assertEquals(mA11yWindowManager.findWindowIdLocked( |
| USER_SYSTEM_ID, windowToken), windowId); |
| } |
| } |
| |
| @Test |
| public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() { |
| // Updates top 2 z-order WindowInfo are whole visible. |
| WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); |
| windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); |
| windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1); |
| windowInfo.regionInScreen.set(0, SCREEN_HEIGHT / 2, |
| SCREEN_WIDTH, SCREEN_HEIGHT); |
| onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); |
| |
| final List<AccessibilityWindowInfo> a11yWindows = |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); |
| final Region outBounds = new Region(); |
| int windowId = a11yWindows.get(0).getId(); |
| |
| mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); |
| assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); |
| assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); |
| |
| windowId = a11yWindows.get(1).getId(); |
| |
| mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); |
| assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); |
| assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); |
| } |
| |
| @Test |
| public void computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion() { |
| // Updates z-order #1 WindowInfo is half visible. |
| WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); |
| windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); |
| |
| onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); |
| final List<AccessibilityWindowInfo> a11yWindows = |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); |
| final Region outBounds = new Region(); |
| int windowId = a11yWindows.get(1).getId(); |
| |
| mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); |
| assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); |
| assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); |
| } |
| |
| @Test |
| public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() { |
| // Since z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible. |
| final List<AccessibilityWindowInfo> a11yWindows = |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); |
| final Region outBounds = new Region(); |
| int windowId = a11yWindows.get(1).getId(); |
| |
| mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); |
| assertTrue(outBounds.getBounds().isEmpty()); |
| } |
| |
| @Test |
| public void computePartialInteractiveRegionForWindow_partialVisible_returnVisibleRegion() { |
| // Updates z-order #0 WindowInfo to have two interact-able areas. |
| Region region = new Region(0, 0, SCREEN_WIDTH, 200); |
| region.op(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, SCREEN_HEIGHT, Region.Op.UNION); |
| WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); |
| windowInfo.regionInScreen.set(region); |
| onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); |
| |
| final List<AccessibilityWindowInfo> a11yWindows = |
| mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); |
| final Region outBounds = new Region(); |
| int windowId = a11yWindows.get(1).getId(); |
| |
| mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); |
| assertFalse(outBounds.getBounds().isEmpty()); |
| assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); |
| assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT - 400)); |
| } |
| |
| @Test |
| public void updateActiveAndA11yFocusedWindow_windowStateChangedEvent_noTracking_shouldUpdate() { |
| final IBinder eventWindowToken = |
| mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1).token; |
| final int eventWindowId = mA11yWindowManager.findWindowIdLocked( |
| USER_SYSTEM_ID, eventWindowToken); |
| when(mMockWindowManagerInternal.getFocusedWindowToken()) |
| .thenReturn(eventWindowToken); |
| |
| final int noUse = 0; |
| mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); |
| mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, |
| eventWindowId, |
| noUse, |
| AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, |
| noUse); |
| assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId)); |
| assertThat(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), |
| is(eventWindowId)); |
| } |
| |
| @Test |
| public void updateActiveAndA11yFocusedWindow_hoverEvent_touchInteract_shouldSetActiveWindow() { |
| final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, |
| DEFAULT_FOCUSED_INDEX + 1); |
| final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); |
| assertThat(currentActiveWindowId, is(not(eventWindowId))); |
| |
| final int noUse = 0; |
| mA11yWindowManager.onTouchInteractionStart(); |
| mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, |
| eventWindowId, |
| noUse, |
| AccessibilityEvent.TYPE_VIEW_HOVER_ENTER, |
| noUse); |
| assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId)); |
| final ArgumentCaptor<AccessibilityEvent> captor = |
| ArgumentCaptor.forClass(AccessibilityEvent.class); |
| verify(mMockA11yEventSender, times(2)) |
| .sendAccessibilityEventForCurrentUserLocked(captor.capture()); |
| assertThat(captor.getAllValues().get(0), |
| allOf(a11yWindowId(currentActiveWindowId), |
| a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); |
| assertThat(captor.getAllValues().get(1), |
| allOf(a11yWindowId(eventWindowId), |
| a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); |
| } |
| |
| @Test |
| public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_shouldUpdateA11yFocus() { |
| final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, |
| DEFAULT_FOCUSED_INDEX); |
| final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId( |
| AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); |
| assertThat(currentA11yFocusedWindowId, is(not(eventWindowId))); |
| |
| final int noUse = 0; |
| mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, |
| eventWindowId, |
| AccessibilityNodeInfo.ROOT_NODE_ID, |
| AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, |
| noUse); |
| assertThat(mA11yWindowManager.getFocusedWindowId( |
| AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId)); |
| final ArgumentCaptor<AccessibilityEvent> captor = |
| ArgumentCaptor.forClass(AccessibilityEvent.class); |
| verify(mMockA11yEventSender, times(2)) |
| .sendAccessibilityEventForCurrentUserLocked(captor.capture()); |
| assertThat(captor.getAllValues().get(0), |
| allOf(a11yWindowId(currentA11yFocusedWindowId), |
| a11yWindowChanges( |
| AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); |
| assertThat(captor.getAllValues().get(1), |
| allOf(a11yWindowId(eventWindowId), |
| a11yWindowChanges( |
| AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); |
| } |
| |
| @Test |
| public void updateActiveAndA11yFocusedWindow_clearA11yFocusEvent_shouldClearA11yFocus() { |
| final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, |
| DEFAULT_FOCUSED_INDEX); |
| final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId( |
| AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); |
| assertThat(currentA11yFocusedWindowId, is(not(eventWindowId))); |
| |
| final int noUse = 0; |
| mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, |
| eventWindowId, |
| AccessibilityNodeInfo.ROOT_NODE_ID, |
| AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, |
| noUse); |
| assertThat(mA11yWindowManager.getFocusedWindowId( |
| AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId)); |
| mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, |
| eventWindowId, |
| AccessibilityNodeInfo.ROOT_NODE_ID, |
| AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, |
| noUse); |
| assertThat(mA11yWindowManager.getFocusedWindowId( |
| AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), |
| is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)); |
| } |
| |
| @Test |
| public void onTouchInteractionEnd_shouldRollbackActiveWindow() { |
| final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, |
| DEFAULT_FOCUSED_INDEX + 1); |
| final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); |
| assertThat(currentActiveWindowId, is(not(eventWindowId))); |
| |
| final int noUse = 0; |
| mA11yWindowManager.onTouchInteractionStart(); |
| mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, |
| eventWindowId, |
| noUse, |
| AccessibilityEvent.TYPE_VIEW_HOVER_ENTER, |
| noUse); |
| assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId)); |
| // AccessibilityEventSender is invoked after active window changed. Reset it. |
| Mockito.reset(mMockA11yEventSender); |
| |
| mA11yWindowManager.onTouchInteractionEnd(); |
| final ArgumentCaptor<AccessibilityEvent> captor = |
| ArgumentCaptor.forClass(AccessibilityEvent.class); |
| verify(mMockA11yEventSender, times(2)) |
| .sendAccessibilityEventForCurrentUserLocked(captor.capture()); |
| assertThat(captor.getAllValues().get(0), |
| allOf(a11yWindowId(eventWindowId), |
| a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); |
| assertThat(captor.getAllValues().get(1), |
| allOf(a11yWindowId(currentActiveWindowId), |
| a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); |
| } |
| |
| @Test |
| public void onTouchInteractionEnd_noServiceInteractiveWindow_shouldClearA11yFocus() |
| throws RemoteException { |
| final IBinder defaultFocusWinToken = |
| mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).token; |
| final int defaultFocusWindowId = mA11yWindowManager.findWindowIdLocked( |
| USER_SYSTEM_ID, defaultFocusWinToken); |
| when(mMockWindowManagerInternal.getFocusedWindowToken()) |
| .thenReturn(defaultFocusWinToken); |
| final int newFocusWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, |
| DEFAULT_FOCUSED_INDEX + 1); |
| final IAccessibilityInteractionConnection mockNewFocusConnection = |
| mA11yWindowManager.getConnectionLocked( |
| USER_SYSTEM_ID, newFocusWindowId).getRemote(); |
| |
| mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); |
| final int noUse = 0; |
| mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, |
| defaultFocusWindowId, |
| noUse, |
| AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, |
| noUse); |
| assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(defaultFocusWindowId)); |
| assertThat(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), |
| is(defaultFocusWindowId)); |
| |
| mA11yWindowManager.onTouchInteractionStart(); |
| mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, |
| newFocusWindowId, |
| noUse, |
| AccessibilityEvent.TYPE_VIEW_HOVER_ENTER, |
| noUse); |
| mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, |
| newFocusWindowId, |
| AccessibilityNodeInfo.ROOT_NODE_ID, |
| AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, |
| noUse); |
| assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(newFocusWindowId)); |
| assertThat(mA11yWindowManager.getFocusedWindowId( |
| AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(newFocusWindowId)); |
| |
| mA11yWindowManager.onTouchInteractionEnd(); |
| mHandler.sendLastMessage(); |
| verify(mockNewFocusConnection).clearAccessibilityFocus(); |
| } |
| |
| @Test |
| public void getPictureInPictureWindow_shouldNotNull() { |
| assertNull(mA11yWindowManager.getPictureInPictureWindowLocked()); |
| mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1).inPictureInPicture = true; |
| onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); |
| |
| assertNotNull(mA11yWindowManager.getPictureInPictureWindowLocked()); |
| } |
| |
| @Test |
| public void notifyOutsideTouch() throws RemoteException { |
| final int targetWindowId = |
| getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 1); |
| final int outsideWindowId = |
| getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); |
| final IAccessibilityInteractionConnection mockRemoteConnection = |
| mA11yWindowManager.getConnectionLocked( |
| USER_SYSTEM_ID, outsideWindowId).getRemote(); |
| mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0).hasFlagWatchOutsideTouch = true; |
| onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); |
| |
| mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId); |
| verify(mockRemoteConnection).notifyOutsideTouch(); |
| } |
| |
| @Test |
| public void addAccessibilityInteractionConnection_profileUser_findInParentUser() |
| throws RemoteException { |
| final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, |
| false, USER_PROFILE); |
| final int windowId = mA11yWindowManager.findWindowIdLocked( |
| USER_PROFILE_PARENT, token.asBinder()); |
| assertTrue(windowId >= 0); |
| } |
| |
| @Test |
| public void getDisplayList() throws RemoteException { |
| // Starts tracking window of second display. |
| startTrackingPerDisplay(SECONDARY_DISPLAY_ID); |
| |
| final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked(); |
| assertTrue(displayList.equals(mExpectedDisplayList)); |
| } |
| |
| @Test |
| public void setAccessibilityWindowIdToSurfaceMetadata() |
| throws RemoteException { |
| final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, |
| true, USER_SYSTEM_ID); |
| int windowId = -1; |
| for (int i = 0; i < mA11yWindowTokens.size(); i++) { |
| if (mA11yWindowTokens.valueAt(i).equals(token)) { |
| windowId = mA11yWindowTokens.keyAt(i); |
| } |
| } |
| assertNotEquals("Returned token is not found in mA11yWindowTokens", -1, windowId); |
| verify(mMockWindowManagerInternal, times(1)).setAccessibilityIdToSurfaceMetadata( |
| token.asBinder(), windowId); |
| |
| mA11yWindowManager.removeAccessibilityInteractionConnection(token); |
| verify(mMockWindowManagerInternal, times(1)).setAccessibilityIdToSurfaceMetadata( |
| token.asBinder(), -1); |
| } |
| |
| private void startTrackingPerDisplay(int displayId) throws RemoteException { |
| ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>(); |
| // Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy |
| // mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos |
| // for the test. |
| int layer = 0; |
| for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) { |
| final IWindow token = addAccessibilityInteractionConnection(displayId, |
| true, USER_SYSTEM_ID); |
| addWindowInfo(windowInfosForDisplay, token, layer++); |
| |
| } |
| for (int i = 0; i < NUM_APP_WINDOWS; i++) { |
| final IWindow token = addAccessibilityInteractionConnection(displayId, |
| false, USER_SYSTEM_ID); |
| addWindowInfo(windowInfosForDisplay, token, layer++); |
| } |
| // Sets up current focused window of display. |
| // Each display has its own current focused window if config_perDisplayFocusEnabled is true. |
| // Otherwise only default display needs to current focused window. |
| if (mSupportPerDisplayFocus || displayId == Display.DEFAULT_DISPLAY) { |
| windowInfosForDisplay.get(DEFAULT_FOCUSED_INDEX).focused = true; |
| } |
| // Turns on windows tracking, and update window info. |
| when(mMockWindowManagerInternal.setWindowsForAccessibilityCallback(eq(displayId), any())) |
| .thenReturn(true); |
| mA11yWindowManager.startTrackingWindows(displayId); |
| // Puts window lists into array. |
| mWindowInfos.put(displayId, windowInfosForDisplay); |
| // Sets the default display is the top focused display and |
| // its current focused window is the top focused window. |
| if (displayId == Display.DEFAULT_DISPLAY) { |
| setTopFocusedWindowAndDisplay(displayId, DEFAULT_FOCUSED_INDEX); |
| } |
| // Invokes callback for sending window lists to A11y framework. |
| onWindowsForAccessibilityChanged(displayId, FORCE_SEND); |
| |
| assertEquals(mA11yWindowManager.getWindowListLocked(displayId).size(), |
| windowInfosForDisplay.size()); |
| } |
| |
| private WindowsForAccessibilityCallback getWindowsForAccessibilityCallbacks(int displayId) { |
| ArgumentCaptor<WindowsForAccessibilityCallback> windowsForAccessibilityCallbacksCaptor = |
| ArgumentCaptor.forClass( |
| WindowManagerInternal.WindowsForAccessibilityCallback.class); |
| verify(mMockWindowManagerInternal) |
| .setWindowsForAccessibilityCallback(eq(displayId), |
| windowsForAccessibilityCallbacksCaptor.capture()); |
| return windowsForAccessibilityCallbacksCaptor.getValue(); |
| } |
| |
| private IWindow addAccessibilityInteractionConnection(int displayId, boolean bGlobal, |
| int userId) throws RemoteException { |
| final IWindow mockWindowToken = Mockito.mock(IWindow.class); |
| final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock( |
| IAccessibilityInteractionConnection.class); |
| final IBinder mockConnectionBinder = Mockito.mock(IBinder.class); |
| final IBinder mockWindowBinder = Mockito.mock(IBinder.class); |
| when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder); |
| when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder); |
| when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId)) |
| .thenReturn(bGlobal); |
| when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder())) |
| .thenReturn(displayId); |
| |
| int windowId = mA11yWindowManager.addAccessibilityInteractionConnection( |
| mockWindowToken, mockA11yConnection, PACKAGE_NAME, userId); |
| mA11yWindowTokens.put(windowId, mockWindowToken); |
| return mockWindowToken; |
| } |
| |
| private void addWindowInfo(ArrayList<WindowInfo> windowInfos, IWindow windowToken, int layer) { |
| final WindowInfo windowInfo = WindowInfo.obtain(); |
| windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION; |
| windowInfo.token = windowToken.asBinder(); |
| windowInfo.layer = layer; |
| windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); |
| windowInfos.add(windowInfo); |
| } |
| |
| private int getWindowIdFromWindowInfosForDisplay(int displayId, int index) { |
| final IBinder windowToken = mWindowInfos.get(displayId).get(index).token; |
| return mA11yWindowManager.findWindowIdLocked( |
| USER_SYSTEM_ID, windowToken); |
| } |
| |
| private void setTopFocusedWindowAndDisplay(int displayId, int index) { |
| // Sets the top focus window. |
| mTopFocusedWindowToken = mWindowInfos.get(displayId).get(index).token; |
| // Sets the top focused display. |
| mTopFocusedDisplayId = displayId; |
| } |
| |
| private void onWindowsForAccessibilityChanged(int displayId, boolean forceSend) { |
| WindowsForAccessibilityCallback callbacks = mCallbackOfWindows.get(displayId); |
| if (callbacks == null) { |
| callbacks = getWindowsForAccessibilityCallbacks(displayId); |
| mCallbackOfWindows.put(displayId, callbacks); |
| } |
| callbacks.onWindowsForAccessibilityChanged(forceSend, mTopFocusedDisplayId, |
| mTopFocusedWindowToken, mWindowInfos.get(displayId)); |
| } |
| |
| private void changeFocusedWindowOnDisplayPerDisplayFocusConfig( |
| int changeFocusedDisplayId, int newFocusedWindowIndex, int oldTopFocusedDisplayId, |
| int oldFocusedWindowIndex) { |
| if (mSupportPerDisplayFocus) { |
| // Gets the old focused window of display which wants to change focused window. |
| WindowInfo focusedWindowInfo = |
| mWindowInfos.get(changeFocusedDisplayId).get(oldFocusedWindowIndex); |
| // Resets the focus of old focused window. |
| focusedWindowInfo.focused = false; |
| // Gets the new window of display which wants to change focused window. |
| focusedWindowInfo = |
| mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex); |
| // Sets the focus of new focused window. |
| focusedWindowInfo.focused = true; |
| } else { |
| // Gets the window of display which wants to change focused window. |
| WindowInfo focusedWindowInfo = |
| mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex); |
| // Sets the focus of new focused window. |
| focusedWindowInfo.focused = true; |
| // Gets the old focused window of old top focused display. |
| focusedWindowInfo = |
| mWindowInfos.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex); |
| // Resets the focus of old focused window. |
| focusedWindowInfo.focused = false; |
| // Changes the top focused display and window. |
| setTopFocusedWindowAndDisplay(changeFocusedDisplayId, newFocusedWindowIndex); |
| } |
| } |
| |
| static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> { |
| private int mWindowId; |
| |
| WindowIdMatcher(int windowId) { |
| super(); |
| mWindowId = windowId; |
| } |
| |
| static WindowIdMatcher a11yWindowId(int windowId) { |
| return new WindowIdMatcher(windowId); |
| } |
| |
| @Override |
| protected boolean matchesSafely(AccessibilityEvent event) { |
| return event.getWindowId() == mWindowId; |
| } |
| |
| @Override |
| public void describeTo(Description description) { |
| description.appendText("Matching to windowId " + mWindowId); |
| } |
| } |
| |
| static class WindowChangesMatcher extends TypeSafeMatcher<AccessibilityEvent> { |
| private int mWindowChanges; |
| |
| WindowChangesMatcher(int windowChanges) { |
| super(); |
| mWindowChanges = windowChanges; |
| } |
| |
| static WindowChangesMatcher a11yWindowChanges(int windowChanges) { |
| return new WindowChangesMatcher(windowChanges); |
| } |
| |
| @Override |
| protected boolean matchesSafely(AccessibilityEvent event) { |
| return event.getWindowChanges() == mWindowChanges; |
| } |
| |
| @Override |
| public void describeTo(Description description) { |
| description.appendText("Matching to window changes " + mWindowChanges); |
| } |
| } |
| } |