1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.accessibility; 18 19 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; 20 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; 21 22 import static com.android.systemui.accessibility.AccessibilityLogger.MagnificationSettingsEvent; 23 import static com.android.systemui.accessibility.WindowMagnificationSettings.MagnificationSize; 24 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; 25 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP; 26 27 import static org.mockito.ArgumentMatchers.any; 28 import static org.mockito.ArgumentMatchers.anyBoolean; 29 import static org.mockito.ArgumentMatchers.anyInt; 30 import static org.mockito.ArgumentMatchers.eq; 31 import static org.mockito.ArgumentMatchers.isNull; 32 import static org.mockito.Mockito.doAnswer; 33 import static org.mockito.Mockito.mock; 34 import static org.mockito.Mockito.never; 35 import static org.mockito.Mockito.verify; 36 import static org.mockito.Mockito.when; 37 38 import android.content.Context; 39 import android.graphics.Rect; 40 import android.hardware.display.DisplayManager; 41 import android.os.RemoteException; 42 import android.testing.AndroidTestingRunner; 43 import android.testing.TestableLooper; 44 import android.view.Display; 45 import android.view.accessibility.AccessibilityManager; 46 import android.view.accessibility.IWindowMagnificationConnection; 47 import android.view.accessibility.IWindowMagnificationConnectionCallback; 48 49 import androidx.test.filters.SmallTest; 50 51 import com.android.systemui.SysuiTestCase; 52 import com.android.systemui.model.SysUiState; 53 import com.android.systemui.recents.OverviewProxyService; 54 import com.android.systemui.settings.FakeDisplayTracker; 55 import com.android.systemui.statusbar.CommandQueue; 56 import com.android.systemui.util.settings.SecureSettings; 57 58 import org.junit.Before; 59 import org.junit.Test; 60 import org.junit.runner.RunWith; 61 import org.mockito.ArgumentCaptor; 62 import org.mockito.Mock; 63 import org.mockito.MockitoAnnotations; 64 65 @SmallTest 66 @RunWith(AndroidTestingRunner.class) 67 @TestableLooper.RunWithLooper 68 public class WindowMagnificationTest extends SysuiTestCase { 69 70 private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; 71 @Mock 72 private AccessibilityManager mAccessibilityManager; 73 @Mock 74 private ModeSwitchesController mModeSwitchesController; 75 @Mock 76 private SysUiState mSysUiState; 77 @Mock 78 private IWindowMagnificationConnectionCallback mConnectionCallback; 79 @Mock 80 private OverviewProxyService mOverviewProxyService; 81 @Mock 82 private SecureSettings mSecureSettings; 83 84 private CommandQueue mCommandQueue; 85 private WindowMagnification mWindowMagnification; 86 private OverviewProxyListener mOverviewProxyListener; 87 private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext); 88 89 @Mock 90 private WindowMagnificationController mWindowMagnificationController; 91 @Mock 92 private MagnificationSettingsController mMagnificationSettingsController; 93 @Mock 94 private AccessibilityLogger mA11yLogger; 95 96 @Before setUp()97 public void setUp() throws Exception { 98 MockitoAnnotations.initMocks(this); 99 getContext().addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager); 100 doAnswer(invocation -> { 101 IWindowMagnificationConnection connection = invocation.getArgument(0); 102 connection.setConnectionCallback(mConnectionCallback); 103 return null; 104 }).when(mAccessibilityManager).setWindowMagnificationConnection( 105 any(IWindowMagnificationConnection.class)); 106 107 when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState); 108 109 doAnswer(invocation -> { 110 mWindowMagnification.mMagnificationSettingsControllerCallback 111 .onSettingsPanelVisibilityChanged(TEST_DISPLAY, /* shown= */ true); 112 return null; 113 }).when(mMagnificationSettingsController).toggleSettingsPanelVisibility(); 114 doAnswer(invocation -> { 115 mWindowMagnification.mMagnificationSettingsControllerCallback 116 .onSettingsPanelVisibilityChanged(TEST_DISPLAY, /* shown= */ false); 117 return null; 118 }).when(mMagnificationSettingsController).closeMagnificationSettings(); 119 120 mCommandQueue = new CommandQueue(getContext(), mDisplayTracker); 121 mWindowMagnification = new WindowMagnification(getContext(), 122 getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController, 123 mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker, 124 getContext().getSystemService(DisplayManager.class), mA11yLogger); 125 mWindowMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier( 126 mContext.getSystemService(DisplayManager.class), mWindowMagnificationController); 127 mWindowMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier( 128 mContext.getSystemService(DisplayManager.class), mMagnificationSettingsController); 129 mWindowMagnification.start(); 130 131 final ArgumentCaptor<OverviewProxyListener> listenerArgumentCaptor = 132 ArgumentCaptor.forClass(OverviewProxyListener.class); 133 verify(mOverviewProxyService).addCallback(listenerArgumentCaptor.capture()); 134 mOverviewProxyListener = listenerArgumentCaptor.getValue(); 135 } 136 137 @Test requestWindowMagnificationConnection_setConnectionAndListener()138 public void requestWindowMagnificationConnection_setConnectionAndListener() { 139 mCommandQueue.requestWindowMagnificationConnection(true); 140 waitForIdleSync(); 141 142 verify(mAccessibilityManager).setWindowMagnificationConnection(any( 143 IWindowMagnificationConnection.class)); 144 145 mCommandQueue.requestWindowMagnificationConnection(false); 146 waitForIdleSync(); 147 148 verify(mAccessibilityManager).setWindowMagnificationConnection(isNull()); 149 } 150 151 @Test onWindowMagnifierBoundsChanged()152 public void onWindowMagnifierBoundsChanged() throws RemoteException { 153 final Rect testBounds = new Rect(0, 0, 500, 600); 154 mCommandQueue.requestWindowMagnificationConnection(true); 155 waitForIdleSync(); 156 157 mWindowMagnification.mWindowMagnifierCallback 158 .onWindowMagnifierBoundsChanged(TEST_DISPLAY, testBounds); 159 160 verify(mConnectionCallback).onWindowMagnifierBoundsChanged(TEST_DISPLAY, testBounds); 161 } 162 163 @Test onPerformScaleAction_enabled_notifyCallback()164 public void onPerformScaleAction_enabled_notifyCallback() throws RemoteException { 165 final float newScale = 4.0f; 166 final boolean updatePersistence = true; 167 mCommandQueue.requestWindowMagnificationConnection(true); 168 waitForIdleSync(); 169 170 mWindowMagnification.mWindowMagnifierCallback 171 .onPerformScaleAction(TEST_DISPLAY, newScale, updatePersistence); 172 173 verify(mConnectionCallback).onPerformScaleAction( 174 eq(TEST_DISPLAY), eq(newScale), eq(updatePersistence)); 175 } 176 177 @Test onAccessibilityActionPerformed_enabled_notifyCallback()178 public void onAccessibilityActionPerformed_enabled_notifyCallback() throws RemoteException { 179 mCommandQueue.requestWindowMagnificationConnection(true); 180 waitForIdleSync(); 181 182 mWindowMagnification.mWindowMagnifierCallback 183 .onAccessibilityActionPerformed(TEST_DISPLAY); 184 185 verify(mConnectionCallback).onAccessibilityActionPerformed(TEST_DISPLAY); 186 } 187 188 @Test onMove_enabled_notifyCallback()189 public void onMove_enabled_notifyCallback() throws RemoteException { 190 mCommandQueue.requestWindowMagnificationConnection(true); 191 waitForIdleSync(); 192 193 mWindowMagnification.mWindowMagnifierCallback.onMove(TEST_DISPLAY); 194 195 verify(mConnectionCallback).onMove(TEST_DISPLAY); 196 } 197 198 @Test onClickSettingsButton_enabled_showPanelForWindowMode()199 public void onClickSettingsButton_enabled_showPanelForWindowMode() { 200 mWindowMagnification.mWindowMagnifierCallback.onClickSettingsButton(TEST_DISPLAY); 201 waitForIdleSync(); 202 203 verify(mMagnificationSettingsController).toggleSettingsPanelVisibility(); 204 verify(mA11yLogger).log( 205 eq(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_OPENED)); 206 } 207 208 @Test onSetMagnifierSize_delegateToMagnifier()209 public void onSetMagnifierSize_delegateToMagnifier() { 210 final @MagnificationSize int index = MagnificationSize.SMALL; 211 mWindowMagnification.mMagnificationSettingsControllerCallback.onSetMagnifierSize( 212 TEST_DISPLAY, index); 213 waitForIdleSync(); 214 215 verify(mWindowMagnificationController).changeMagnificationSize(eq(index)); 216 verify(mA11yLogger).log( 217 eq(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_WINDOW_SIZE_SELECTED)); 218 } 219 220 @Test onSetDiagonalScrolling_delegateToMagnifier()221 public void onSetDiagonalScrolling_delegateToMagnifier() { 222 mWindowMagnification.mMagnificationSettingsControllerCallback.onSetDiagonalScrolling( 223 TEST_DISPLAY, /* enable= */ true); 224 waitForIdleSync(); 225 226 verify(mWindowMagnificationController).setDiagonalScrolling(eq(true)); 227 } 228 229 @Test onEditMagnifierSizeMode_windowActivated_delegateToMagnifier()230 public void onEditMagnifierSizeMode_windowActivated_delegateToMagnifier() { 231 when(mWindowMagnificationController.isActivated()).thenReturn(true); 232 mWindowMagnification.mMagnificationSettingsControllerCallback.onEditMagnifierSizeMode( 233 TEST_DISPLAY, /* enable= */ true); 234 waitForIdleSync(); 235 236 verify(mWindowMagnificationController).setEditMagnifierSizeMode(eq(true)); 237 verify(mA11yLogger).log( 238 eq(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_SIZE_EDITING_ACTIVATED)); 239 240 mWindowMagnification.mMagnificationSettingsControllerCallback.onEditMagnifierSizeMode( 241 TEST_DISPLAY, /* enable= */ false); 242 waitForIdleSync(); 243 verify(mA11yLogger).log( 244 eq(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_SIZE_EDITING_ACTIVATED)); 245 verify(mA11yLogger).log( 246 eq(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_SIZE_EDITING_DEACTIVATED)); 247 } 248 249 @Test onMagnifierScale_notifyCallback()250 public void onMagnifierScale_notifyCallback() throws RemoteException { 251 mCommandQueue.requestWindowMagnificationConnection(true); 252 waitForIdleSync(); 253 final float scale = 3.0f; 254 final boolean updatePersistence = false; 255 mWindowMagnification.mMagnificationSettingsControllerCallback.onMagnifierScale( 256 TEST_DISPLAY, scale, updatePersistence); 257 258 verify(mConnectionCallback).onPerformScaleAction( 259 eq(TEST_DISPLAY), eq(scale), eq(updatePersistence)); 260 } 261 262 @Test onModeSwitch_windowEnabledAndSwitchToFullscreen_hidePanelAndNotifyCallback()263 public void onModeSwitch_windowEnabledAndSwitchToFullscreen_hidePanelAndNotifyCallback() 264 throws RemoteException { 265 when(mWindowMagnificationController.isActivated()).thenReturn(true); 266 mCommandQueue.requestWindowMagnificationConnection(true); 267 waitForIdleSync(); 268 269 mWindowMagnification.mMagnificationSettingsControllerCallback.onModeSwitch( 270 TEST_DISPLAY, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 271 waitForIdleSync(); 272 273 verify(mMagnificationSettingsController).closeMagnificationSettings(); 274 verify(mA11yLogger).log( 275 eq(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_CLOSED)); 276 verify(mConnectionCallback).onChangeMagnificationMode(eq(TEST_DISPLAY), 277 eq(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)); 278 } 279 280 @Test onModeSwitch_switchToSameMode_doNothing()281 public void onModeSwitch_switchToSameMode_doNothing() 282 throws RemoteException { 283 when(mWindowMagnificationController.isActivated()).thenReturn(true); 284 mCommandQueue.requestWindowMagnificationConnection(true); 285 waitForIdleSync(); 286 287 mWindowMagnification.mMagnificationSettingsControllerCallback.onModeSwitch( 288 TEST_DISPLAY, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); 289 waitForIdleSync(); 290 291 verify(mMagnificationSettingsController, never()).closeMagnificationSettings(); 292 verify(mConnectionCallback, never()).onChangeMagnificationMode(eq(TEST_DISPLAY), 293 /* magnificationMode = */ anyInt()); 294 } 295 296 @Test onSettingsPanelVisibilityChanged_windowActivated_delegateToMagnifier()297 public void onSettingsPanelVisibilityChanged_windowActivated_delegateToMagnifier() { 298 when(mWindowMagnificationController.isActivated()).thenReturn(true); 299 final boolean shown = false; 300 mWindowMagnification.mMagnificationSettingsControllerCallback 301 .onSettingsPanelVisibilityChanged(TEST_DISPLAY, shown); 302 waitForIdleSync(); 303 304 verify(mWindowMagnificationController).updateDragHandleResourcesIfNeeded(eq(shown)); 305 verify(mA11yLogger).log( 306 eq(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_CLOSED)); 307 } 308 309 @Test overviewProxyIsConnected_noController_resetFlag()310 public void overviewProxyIsConnected_noController_resetFlag() { 311 mOverviewProxyListener.onConnectionChanged(true); 312 313 verify(mSysUiState).setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false); 314 verify(mSysUiState).commitUpdate(mContext.getDisplayId()); 315 } 316 317 @Test overviewProxyIsConnected_controllerIsAvailable_updateSysUiStateFlag()318 public void overviewProxyIsConnected_controllerIsAvailable_updateSysUiStateFlag() { 319 final WindowMagnificationController mController = mock(WindowMagnificationController.class); 320 mWindowMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier( 321 mContext.getSystemService(DisplayManager.class), mController); 322 mWindowMagnification.mMagnificationControllerSupplier.get(TEST_DISPLAY); 323 324 mOverviewProxyListener.onConnectionChanged(true); 325 326 verify(mController).updateSysUIStateFlag(); 327 } 328 329 private static class FakeControllerSupplier extends 330 DisplayIdIndexSupplier<WindowMagnificationController> { 331 332 private final WindowMagnificationController mController; 333 FakeControllerSupplier(DisplayManager displayManager, WindowMagnificationController controller)334 FakeControllerSupplier(DisplayManager displayManager, 335 WindowMagnificationController controller) { 336 super(displayManager); 337 mController = controller; 338 } 339 340 @Override createInstance(Display display)341 protected WindowMagnificationController createInstance(Display display) { 342 return mController; 343 } 344 } 345 346 private static class FakeSettingsSupplier extends 347 DisplayIdIndexSupplier<MagnificationSettingsController> { 348 349 private final MagnificationSettingsController mController; 350 FakeSettingsSupplier(DisplayManager displayManager, MagnificationSettingsController controller)351 FakeSettingsSupplier(DisplayManager displayManager, 352 MagnificationSettingsController controller) { 353 super(displayManager); 354 mController = controller; 355 } 356 357 @Override createInstance(Display display)358 protected MagnificationSettingsController createInstance(Display display) { 359 return mController; 360 } 361 } 362 } 363