From 49284c318dd574f0ff2388d6abac9459a312af72 Mon Sep 17 00:00:00 2001 From: ryanlwlin Date: Tue, 23 Mar 2021 15:21:56 +0800 Subject: [PATCH] update switch UI if display magnification request is from external Display magnifier could be magipulated by public API. This request should be treated as an user interaction. We should also update the magnification button in such circumstance. Besides, we also disable display magnification that is requested by AccessibilityService when window magnification is enabled. In the test part, we also verify the shorct trigged in full-screen mode. Bug: 179446412 Test: atest MagnificationControllerTest manually Test: Use voice access to controll display magnifier and chek if switch button is shown. Change-Id: I729ea6fbf921ba6bd735c62198a2b8caee6ec4d7 --- .../MagnificationController.java | 30 ++- .../WindowMagnificationManager.java | 7 +- .../MagnificationControllerTest.java | 177 +++++++++++++++--- .../WindowMagnificationManagerTest.java | 4 +- 4 files changed, 176 insertions(+), 42 deletions(-) diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 878ebc5bbfbde..80408518d84e8 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -53,10 +53,13 @@ import com.android.server.accessibility.AccessibilityManagerService; * is triggered. *
  • 4. {@link #onTripleTapped} updates magnification switch UI depending on magnification * capabilities and magnification active state when triple-tap gesture is detected.
  • + *
  • 4. {@link #onRequestMagnificationSpec} updates magnification switch UI depending on + * magnification capabilities and magnification active state when new magnification spec is + * changed by external request from calling public APIs.
  • * * - * Note Updates magnification switch UI when magnification mode transition - * is done {@link DisableMagnificationCallback#onResult}. + * Note Updates magnification switch UI when magnification mode transition + * is done and before invoking {@link TransitionCallBack#onResult}. */ public class MagnificationController implements WindowMagnificationManager.Callback, MagnificationGestureHandler.Callback, @@ -205,21 +208,22 @@ public class MagnificationController implements WindowMagnificationManager.Callb @Override public void onRequestMagnificationSpec(int displayId, int serviceId) { + final WindowMagnificationManager windowMagnificationManager; synchronized (mLock) { if (serviceId == AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID) { return; } - if (mWindowMagnificationMgr == null - || !mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId)) { - return; - } + updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + windowMagnificationManager = mWindowMagnificationMgr; + } + if (windowMagnificationManager != null) { mWindowMagnificationMgr.disableWindowMagnification(displayId, false); } } // TODO : supporting multi-display (b/182227245). @Override - public void onWindowMagnificationActivationState(boolean activated) { + public void onWindowMagnificationActivationState(int displayId, boolean activated) { if (activated) { mWindowModeEnabledTime = SystemClock.uptimeMillis(); @@ -227,6 +231,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; } logMagnificationModeWithImeOnIfNeeded(); + disableFullScreenMagnificationIfNeeded(displayId); } else { logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, SystemClock.uptimeMillis() - mWindowModeEnabledTime); @@ -237,6 +242,17 @@ public class MagnificationController implements WindowMagnificationManager.Callb } } + private void disableFullScreenMagnificationIfNeeded(int displayId) { + final FullScreenMagnificationController fullScreenMagnificationController = + getFullScreenMagnificationController(); + // Internal request may be for transition, so we just need to check external request. + final boolean isMagnifyByExternalRequest = + fullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId) > 0; + if (isMagnifyByExternalRequest) { + fullScreenMagnificationController.reset(displayId, false); + } + } + @Override public void onFullScreenMagnificationActivationState(boolean activated) { if (activated) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java index ded601e70ff12..86f61eef2e7a6 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -97,9 +97,10 @@ public class WindowMagnificationManager implements /** * Called when the state of the magnification activation is changed. * + * @param displayId The logical display id. * @param activated {@code true} if the magnification is activated, otherwise {@code false}. */ - void onWindowMagnificationActivationState(boolean activated); + void onWindowMagnificationActivationState(int displayId, boolean activated); } private final Callback mCallback; @@ -285,7 +286,7 @@ public class WindowMagnificationManager implements } if (enabled) { - mCallback.onWindowMagnificationActivationState(true); + mCallback.onWindowMagnificationActivationState(displayId, true); } } @@ -321,7 +322,7 @@ public class WindowMagnificationManager implements } if (disabled) { - mCallback.onWindowMagnificationActivationState(false); + mCallback.onWindowMagnificationActivationState(displayId, false); } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index 3dff36e8f379d..81be2e7d8cea6 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -64,6 +64,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; /** * Tests for MagnificationController. @@ -72,6 +73,7 @@ import org.mockito.MockitoAnnotations; public class MagnificationControllerTest { private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; + private static final int TEST_SERVICE_ID = 1; private static final Region MAGNIFICATION_REGION = new Region(0, 0, 500, 600); private static final float MAGNIFIED_CENTER_X = 100; private static final float MAGNIFIED_CENTER_Y = 200; @@ -96,6 +98,7 @@ public class MagnificationControllerTest { private WindowMagnificationManager mWindowMagnificationManager; private MockContentResolver mMockResolver; private MagnificationController mMagnificationController; + private FullScreenMagnificationControllerStubber mScreenMagnificationControllerStubber; @Before public void setUp() throws Exception { @@ -112,8 +115,11 @@ public class MagnificationControllerTest { mock(WindowMagnificationManager.Callback.class))); mMockConnection = new MockWindowMagnificationConnection(true); mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mScreenMagnificationControllerStubber = new FullScreenMagnificationControllerStubber( + mScreenMagnificationController); mMagnificationController = spy(new MagnificationController(mService, new Object(), mContext, mScreenMagnificationController, mWindowMagnificationManager)); + mMagnificationController.setMagnificationCapabilities( Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL); } @@ -240,9 +246,11 @@ public class MagnificationControllerTest { MODE_FULLSCREEN, mTransitionCallBack); - verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY, - DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, - true, MAGNIFICATION_GESTURE_HANDLER_ID); + assertEquals(DEFAULT_SCALE, mScreenMagnificationController.getScale(TEST_DISPLAY), 0); + assertEquals(MAGNIFIED_CENTER_X, mScreenMagnificationController.getCenterX(TEST_DISPLAY), + 0); + assertEquals(MAGNIFIED_CENTER_Y, mScreenMagnificationController.getCenterY(TEST_DISPLAY), + 0); verify(mTransitionCallBack).onResult(true); } @@ -286,12 +294,31 @@ public class MagnificationControllerTest { public void onMagnificationRequest_windowMagnifying_disableWindow() throws RemoteException { setMagnificationEnabled(MODE_WINDOW); - mMagnificationController.onRequestMagnificationSpec(TEST_DISPLAY, 1); + mMagnificationController.onRequestMagnificationSpec(TEST_DISPLAY, TEST_SERVICE_ID); mMockConnection.invokeCallbacks(); assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); } + @Test + public void magnifyThroughExternalRequest_showMagnificationButton() { + mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, + MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, false, TEST_SERVICE_ID); + mMagnificationController.onRequestMagnificationSpec(TEST_DISPLAY, TEST_SERVICE_ID); + + verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY), + eq(MODE_FULLSCREEN)); + } + + @Test + public void setScaleOneThroughExternalRequest_removeMagnificationButton() { + mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, 1.0f, + MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, false, TEST_SERVICE_ID); + mMagnificationController.onRequestMagnificationSpec(TEST_DISPLAY, TEST_SERVICE_ID); + + verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY)); + } + @Test public void onPerformScaleAction_magnifierEnabled_handleScaleChange() throws RemoteException { final float newScale = 4.0f; @@ -305,14 +332,25 @@ public class MagnificationControllerTest { @Test public void onWindowMagnificationActivationState_windowActivated_logWindowDuration() { - mMagnificationController.onWindowMagnificationActivationState(true); + mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true); - mMagnificationController.onWindowMagnificationActivationState(false); + mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, false); verify(mMagnificationController).logMagnificationUsageState( eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW), anyLong()); } + @Test + public void onWinodwModeActivated_fullScreenIsActivatedByExternal_fullScreenIsDisabled() { + mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, + DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, + true, TEST_SERVICE_ID); + + mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true); + + assertFalse(mScreenMagnificationController.isMagnifying(TEST_DISPLAY)); + } + @Test public void onFullScreenMagnificationActivationState_fullScreenActivated_logFullScreenDuration() { @@ -417,6 +455,18 @@ public class MagnificationControllerTest { eq(MODE_FULLSCREEN)); } + @Test + public void triggerShortcutToShowMagnificationBound_fullscreenMode_showMagnificationButton() { + setMagnificationModeSettings(MODE_FULLSCREEN); + + when(mScreenMagnificationController.isForceShowMagnifiableBounds(TEST_DISPLAY)).thenReturn( + true); + mMagnificationController.onShortcutTriggered(TEST_DISPLAY, MODE_FULLSCREEN); + + verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY), + eq(MODE_FULLSCREEN)); + } + @Test public void onShortcutTriggered_windowModeDisabled_removeMagnificationButton() throws RemoteException { @@ -488,7 +538,7 @@ public class MagnificationControllerTest { @Test public void imeWindowStateShown_windowMagnifying_logWindowMode() { - mMagnificationController.onWindowMagnificationActivationState(true); + mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true); mMagnificationController.onImeWindowVisibilityChanged(true); @@ -522,26 +572,19 @@ public class MagnificationControllerTest { @Test public void imeWindowStateHidden_fullScreenMagnifying_noLogAnyMode() { - mMagnificationController.onWindowMagnificationActivationState(true); + mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true); verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt()); } private void setMagnificationEnabled(int mode) throws RemoteException { - setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y); } private void setMagnificationEnabled(int mode, float centerX, float centerY) throws RemoteException { setMagnificationModeSettings(mode); - Mockito.reset(mScreenMagnificationController); - doAnswer(invocation -> { - final Region outRegion = invocation.getArgument(1); - outRegion.set(MAGNIFICATION_REGION); - return null; - }).when(mScreenMagnificationController).getMagnificationRegion(anyInt(), any(Region.class)); - + mScreenMagnificationControllerStubber.resetAndStubMethods(); final boolean windowMagnifying = mWindowMagnificationManager.isWindowMagnifierEnabled( TEST_DISPLAY); if (windowMagnifying) { @@ -549,21 +592,9 @@ public class MagnificationControllerTest { mMockConnection.invokeCallbacks(); } if (mode == MODE_FULLSCREEN) { - when(mScreenMagnificationController.isMagnifying(TEST_DISPLAY)).thenReturn(true); - when(mScreenMagnificationController.isForceShowMagnifiableBounds( - TEST_DISPLAY)).thenReturn(true); - when(mScreenMagnificationController.getPersistedScale()).thenReturn(DEFAULT_SCALE); - when(mScreenMagnificationController.getScale(TEST_DISPLAY)).thenReturn(DEFAULT_SCALE); - when(mScreenMagnificationController.getCenterX(TEST_DISPLAY)).thenReturn( - centerX); - when(mScreenMagnificationController.getCenterY(TEST_DISPLAY)).thenReturn( - centerY); + mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, centerX, + centerY, true, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); } else { - doAnswer(invocation -> { - when(mScreenMagnificationController.isMagnifying(TEST_DISPLAY)).thenReturn(true); - return null; - }).when(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), - eq(DEFAULT_SCALE), anyFloat(), anyFloat(), anyBoolean(), anyInt()); mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE, centerX, centerY, null); mMockConnection.invokeCallbacks(); @@ -574,4 +605,90 @@ public class MagnificationControllerTest { Settings.Secure.putIntForUser(mMockResolver, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, mode, CURRENT_USER_ID); } + + /** + * Stubs public methods to simulate the real beahviours. + */ + private static class FullScreenMagnificationControllerStubber { + private final FullScreenMagnificationController mScreenMagnificationController; + private boolean mIsMagnifying = false; + private float mScale = 1.0f; + private float mCenterX = 0; + private float mCenterY = 0; + private int mServiceId = -1; + + FullScreenMagnificationControllerStubber( + FullScreenMagnificationController screenMagnificationController) { + mScreenMagnificationController = screenMagnificationController; + resetCenter(); + stubMethods(); + } + + private void stubMethods() { + doAnswer(invocation -> mIsMagnifying).when(mScreenMagnificationController).isMagnifying( + TEST_DISPLAY); + doAnswer(invocation -> mIsMagnifying).when( + mScreenMagnificationController).isForceShowMagnifiableBounds(TEST_DISPLAY); + doAnswer(invocation -> mScale).when(mScreenMagnificationController).getPersistedScale(); + doAnswer(invocation -> mScale).when(mScreenMagnificationController).getScale( + TEST_DISPLAY); + doAnswer(invocation -> mCenterX).when(mScreenMagnificationController).getCenterX( + TEST_DISPLAY); + doAnswer(invocation -> mCenterY).when(mScreenMagnificationController).getCenterY( + TEST_DISPLAY); + doAnswer(invocation -> mServiceId).when( + mScreenMagnificationController).getIdOfLastServiceToMagnify(TEST_DISPLAY); + + doAnswer(invocation -> { + final Region outRegion = invocation.getArgument(1); + outRegion.set(MAGNIFICATION_REGION); + return null; + }).when(mScreenMagnificationController).getMagnificationRegion(anyInt(), + any(Region.class)); + + Answer setScaleAndCenterStubAnswer = invocation -> { + final float scale = invocation.getArgument(1); + mScale = Float.isNaN(scale) ? mScale : scale; + mIsMagnifying = mScale > 1.0f; + if (mIsMagnifying) { + mCenterX = invocation.getArgument(2); + mCenterY = invocation.getArgument(3); + mServiceId = invocation.getArgument(5); + } else { + mServiceId = -1; + resetCenter(); + } + return true; + }; + doAnswer(setScaleAndCenterStubAnswer).when( + mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), + anyFloat(), anyFloat(), anyFloat(), any(), anyInt()); + + doAnswer(setScaleAndCenterStubAnswer).when( + mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), + anyFloat(), anyFloat(), anyFloat(), anyBoolean(), anyInt()); + + Answer resetStubAnswer = invocation -> { + mScale = 1.0f; + mIsMagnifying = false; + mServiceId = -1; + resetCenter(); + return true; + }; + doAnswer(resetStubAnswer).when(mScreenMagnificationController).reset(eq(TEST_DISPLAY), + any()); + doAnswer(resetStubAnswer).when(mScreenMagnificationController).reset(eq(TEST_DISPLAY), + anyBoolean()); + } + + private void resetCenter() { + mCenterX = MAGNIFICATION_REGION.getBounds().exactCenterX(); + mCenterY = MAGNIFICATION_REGION.getBounds().exactCenterY(); + } + + public void resetAndStubMethods() { + Mockito.reset(mScreenMagnificationController); + stubMethods(); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java index 955217c7d93f8..ffa0185b45420 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java @@ -397,7 +397,7 @@ public class WindowMagnificationManagerTest { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN); - verify(mMockCallback).onWindowMagnificationActivationState(eq(true)); + verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, true); } @Test @@ -406,7 +406,7 @@ public class WindowMagnificationManagerTest { mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN); mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, true); - verify(mMockCallback).onWindowMagnificationActivationState(eq(false)); + verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, false); } private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {