summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java34
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java237
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java62
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java279
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java91
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java19
7 files changed, 712 insertions, 25 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index c41f4007aa59..80e9703e0e62 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -120,6 +120,7 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
+import com.android.server.accessibility.magnification.MagnificationTransitionController;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -258,6 +259,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private Point mTempPoint = new Point();
private boolean mIsAccessibilityButtonShown;
+ private MagnificationTransitionController mMagnificationTransitionController;
private AccessibilityUserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
@@ -302,6 +304,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mA11yWindowManager = a11yWindowManager;
mA11yDisplayListener = a11yDisplayListener;
mWindowMagnificationMgr = windowMagnificationMgr;
+ mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
init();
}
@@ -321,6 +324,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
+ mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
init();
}
@@ -1550,9 +1554,26 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (fallBackMagnificationModeSettingsLocked(userState)) {
return;
}
- mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::notifyRefreshMagnificationModeToInputFilter,
- this));
+ mMagnificationTransitionController.transitionMagnificationModeLocked(
+ Display.DEFAULT_DISPLAY, userState.getMagnificationModeLocked(),
+ this::onMagnificationTransitionEndedLocked);
+ }
+
+ /**
+ * Called when the magnification mode transition is completed.
+ */
+ void onMagnificationTransitionEndedLocked(boolean success) {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+ final int previousMode = userState.getMagnificationModeLocked()
+ ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
+ if (!success && previousMode != 0) {
+ userState.setMagnificationModeLocked(previousMode);
+ persistMagnificationModeSettingLocked(previousMode);
+ } else {
+ mMainHandler.sendMessage(obtainMessage(
+ AccessibilityManagerService::notifyRefreshMagnificationModeToInputFilter,
+ this));
+ }
}
private void notifyRefreshMagnificationModeToInputFilter() {
@@ -2962,7 +2983,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
getWindowMagnificationMgr().setConnection(connection);
}
- WindowMagnificationManager getWindowMagnificationMgr() {
+ /**
+ * Getter of {@link WindowMagnificationManager}.
+ *
+ * @return WindowMagnificationManager
+ */
+ public WindowMagnificationManager getWindowMagnificationMgr() {
synchronized (mLock) {
if (mWindowMagnificationMgr == null) {
mWindowMagnificationMgr = new WindowMagnificationManager(mContext, mCurrentUserId);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java
new file mode 100644
index 000000000000..af4b34f9613a
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2020 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.magnification;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.accessibility.MagnificationAnimationCallback;
+
+import com.android.server.accessibility.AccessibilityManagerService;
+
+/**
+ * Handles magnification mode transition.
+ */
+public class MagnificationTransitionController {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "MagnificationController";
+ private final AccessibilityManagerService mAms;
+ private final PointF mTempPoint = new PointF();
+ private final Object mLock;
+ private final SparseArray<DisableMagnificationCallback>
+ mMagnificationEndRunnableSparseArray = new SparseArray();
+
+
+ /**
+ * A callback to inform the magnification transition result.
+ */
+ public interface TransitionCallBack {
+ /**
+ * Invoked when the transition ends.
+ * @param success {@code true} if the transition success.
+ */
+ void onResult(boolean success);
+ }
+
+ public MagnificationTransitionController(AccessibilityManagerService ams, Object lock) {
+ mAms = ams;
+ mLock = lock;
+ }
+
+ /**
+ * Transitions to the target Magnification mode with current center of the magnification mode
+ * if it is available.
+ *
+ * @param displayId The logical display
+ * @param targetMode The target magnification mode
+ * @param transitionCallBack The callback invoked when the transition is finished.
+ */
+ public void transitionMagnificationModeLocked(int displayId, int targetMode,
+ @NonNull TransitionCallBack transitionCallBack) {
+ final PointF magnificationCenter = getCurrentMagnificationBoundsCenterLocked(displayId,
+ targetMode);
+ final DisableMagnificationCallback animationCallback =
+ getDisableMagnificationEndRunnableLocked(displayId);
+ if (magnificationCenter == null && animationCallback == null) {
+ transitionCallBack.onResult(true);
+ return;
+ }
+
+ if (animationCallback != null) {
+ if (animationCallback.mCurrentMode == targetMode) {
+ animationCallback.restoreToCurrentMagnificationMode();
+ return;
+ }
+ Slog.w(TAG, "request during transition, abandon current:"
+ + animationCallback.mTargetMode);
+ animationCallback.setExpiredAndRemoveFromListLocked();
+ }
+
+ if (magnificationCenter == null) {
+ Slog.w(TAG, "Invalid center, ignore it");
+ transitionCallBack.onResult(true);
+ return;
+ }
+ final FullScreenMagnificationController screenMagnificationController =
+ getFullScreenMagnificationController();
+ final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationManager();
+ final float scale = windowMagnificationMgr.getPersistedScale();
+ final DisableMagnificationCallback animationEndCallback =
+ new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
+ scale, magnificationCenter);
+ if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
+ screenMagnificationController.reset(displayId, animationEndCallback);
+ } else {
+ windowMagnificationMgr.disableWindowMagnification(displayId, false,
+ animationEndCallback);
+ }
+ setDisableMagnificationCallbackLocked(displayId, animationEndCallback);
+ }
+
+ private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
+ int displayId) {
+ return mMagnificationEndRunnableSparseArray.get(displayId);
+ }
+
+ private void setDisableMagnificationCallbackLocked(int displayId,
+ @Nullable DisableMagnificationCallback callback) {
+ mMagnificationEndRunnableSparseArray.put(displayId, callback);
+ if (DEBUG) {
+ Slog.d(TAG, "setDisableMagnificationCallbackLocked displayId = " + displayId
+ + ", callback = " + callback);
+ }
+ }
+
+ private FullScreenMagnificationController getFullScreenMagnificationController() {
+ return mAms.getFullScreenMagnificationController();
+ }
+
+ private WindowMagnificationManager getWindowMagnificationManager() {
+ return mAms.getWindowMagnificationMgr();
+ }
+
+ private @Nullable
+ PointF getCurrentMagnificationBoundsCenterLocked(int displayId, int targetMode) {
+ if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
+ final WindowMagnificationManager magnificationManager = getWindowMagnificationManager();
+ if (!magnificationManager.isWindowMagnifierEnabled(displayId)) {
+ return null;
+ }
+ mTempPoint.set(magnificationManager.getCenterX(displayId),
+ magnificationManager.getCenterY(displayId));
+ } else {
+ final FullScreenMagnificationController screenMagnificationController =
+ getFullScreenMagnificationController();
+ if (!screenMagnificationController.isMagnifying(displayId)) {
+ return null;
+ }
+ mTempPoint.set(screenMagnificationController.getCenterX(displayId),
+ screenMagnificationController.getCenterY(displayId));
+ }
+ return mTempPoint;
+ }
+
+ private final class DisableMagnificationCallback implements
+ MagnificationAnimationCallback {
+ private final TransitionCallBack mTransitionCallBack;
+ private boolean mExpired = false;
+ private final int mDisplayId;
+ private final int mTargetMode;
+ private final int mCurrentMode;
+ private final float mCurrentScale;
+ private final PointF mCurrentCenter = new PointF();
+
+ DisableMagnificationCallback(TransitionCallBack transitionCallBack,
+ int displayId, int targetMode, float scale, PointF currentCenter) {
+ mTransitionCallBack = transitionCallBack;
+ mDisplayId = displayId;
+ mTargetMode = targetMode;
+ mCurrentMode = mTargetMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
+ mCurrentScale = scale;
+ mCurrentCenter.set(currentCenter);
+ }
+
+ @Override
+ public void onResult(boolean success) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onResult success = " + success);
+ }
+ if (mExpired) {
+ return;
+ }
+ setExpiredAndRemoveFromListLocked();
+ if (success) {
+ adjustCurrentCenterIfNeededLocked();
+ applyMagnificationModeLocked(mTargetMode);
+ }
+ mTransitionCallBack.onResult(success);
+ }
+ }
+
+ private void adjustCurrentCenterIfNeededLocked() {
+ if (mTargetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
+ return;
+ }
+ final Region outRegion = new Region();
+ getFullScreenMagnificationController().getMagnificationRegion(mDisplayId, outRegion);
+ if (outRegion.contains((int) mCurrentCenter.x, (int) mCurrentCenter.y)) {
+ return;
+ }
+ final Rect bounds = outRegion.getBounds();
+ mCurrentCenter.set(bounds.exactCenterX(), bounds.exactCenterY());
+ }
+
+ void restoreToCurrentMagnificationMode() {
+ synchronized (mLock) {
+ if (mExpired) {
+ return;
+ }
+ setExpiredAndRemoveFromListLocked();
+ applyMagnificationModeLocked(mCurrentMode);
+ mTransitionCallBack.onResult(true);
+ }
+ }
+
+ void setExpiredAndRemoveFromListLocked() {
+ mExpired = true;
+ setDisableMagnificationCallbackLocked(mDisplayId, null);
+ }
+
+ private void applyMagnificationModeLocked(int mode) {
+ if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
+ getFullScreenMagnificationController().setScaleAndCenter(mDisplayId,
+ mCurrentScale, mCurrentCenter.x,
+ mCurrentCenter.y, true,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ } else {
+ getWindowMagnificationManager().enableWindowMagnification(mDisplayId,
+ mCurrentScale, mCurrentCenter.x,
+ mCurrentCenter.y);
+ }
+ }
+ }
+}
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 d78d99e0a74b..54618dc6fdce 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -90,7 +90,7 @@ public class WindowMagnificationManager implements
*/
public void setConnection(@Nullable IWindowMagnificationConnection connection) {
synchronized (mLock) {
- //Reset connectionWrapper.
+ // Reset connectionWrapper.
if (mConnectionWrapper != null) {
mConnectionWrapper.setConnectionCallback(null);
if (mConnectionCallback != null) {
@@ -302,7 +302,8 @@ public class WindowMagnificationManager implements
* @param displayId The logical display id.
* @return {@code true} if the window magnification is enabled.
*/
- boolean isWindowMagnifierEnabled(int displayId) {
+ @VisibleForTesting
+ public boolean isWindowMagnifierEnabled(int displayId) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -398,6 +399,38 @@ public class WindowMagnificationManager implements
}
/**
+ * Returns the screen-relative X coordinate of the center of the magnified bounds.
+ *
+ * @param displayId The logical display id
+ * @return the X coordinate. {@link Float#NaN} if the window magnification is not enabled.
+ */
+ float getCenterX(int displayId) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return Float.NaN;
+ }
+ return magnifier.getCenterX();
+ }
+ }
+
+ /**
+ * Returns the screen-relative Y coordinate of the center of the magnified bounds.
+ *
+ * @param displayId The logical display id
+ * @return the Y coordinate. {@link Float#NaN} if the window magnification is not enabled.
+ */
+ float getCenterY(int displayId) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return Float.NaN;
+ }
+ return magnifier.getCenterY();
+ }
+ }
+
+ /**
* Creates the windowMagnifier based on the specified display and stores it.
*
* @param displayId logical display id.
@@ -436,11 +469,13 @@ public class WindowMagnificationManager implements
@Override
public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
- WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
- if (magnifier == null) {
- magnifier = createWindowMagnifier(displayId);
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ magnifier = createWindowMagnifier(displayId);
+ }
+ magnifier.onSourceBoundsChanged(sourceBounds);
}
- magnifier.onSourceBoundsChanged(sourceBounds);
}
@Override
@@ -469,9 +504,9 @@ public class WindowMagnificationManager implements
private boolean mEnabled;
private final WindowMagnificationManager mWindowMagnificationManager;
- //Records the bounds of window magnification.
+ // Records the bounds of window magnification.
private final Rect mBounds = new Rect();
- //The magnified bounds on the screen.
+ // The magnified bounds on the screen.
private final Rect mSourceBounds = new Rect();
WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
@@ -553,9 +588,20 @@ public class WindowMagnificationManager implements
mEnabled = false;
}
+ @GuardedBy("mLock")
public void onSourceBoundsChanged(Rect sourceBounds) {
mSourceBounds.set(sourceBounds);
}
+
+ @GuardedBy("mLock")
+ float getCenterX() {
+ return mEnabled ? mSourceBounds.exactCenterX() : Float.NaN;
+ }
+
+ @GuardedBy("mLock")
+ float getCenterY() {
+ return mEnabled ? mSourceBounds.exactCenterY() : Float.NaN;
+ }
}
private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 23a37c116c6d..b7355ce92c28 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -230,4 +230,19 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
verify(mMockWindowMagnificationMgr, never()).showMagnificationButton(anyInt(),
anyInt());
}
+
+ @SmallTest
+ public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+ userState.setMagnificationModeLocked(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+ mA11yms.onMagnificationTransitionEndedLocked(false);
+
+ assertEquals(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
+ userState.getMagnificationModeLocked());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java
new file mode 100644
index 000000000000..cd8e39cfd2e7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2020 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.magnification;
+
+import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.view.Display;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.accessibility.AccessibilityManagerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for MagnificationController.
+ */
+@RunWith(AndroidJUnit4.class)
+public class MagnificationTransitionControllerTest {
+
+ private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
+ 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;
+ private static final float DEFAULT_SCALE = 3f;
+ private static final int CURRENT_USER_ID = UserHandle.USER_CURRENT;
+ private static final int MODE_WINDOW = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+ private static final int MODE_FULLSCREEN =
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+
+ @Mock private AccessibilityManagerService mService;
+ @Mock private MagnificationTransitionController.TransitionCallBack mTransitionCallBack;
+ @Mock private Context mContext;
+ @Mock private FullScreenMagnificationController mScreenMagnificationController;
+ @Captor private ArgumentCaptor<MagnificationAnimationCallback> mCallbackArgumentCaptor;
+
+ private MockWindowMagnificationConnection mMockConnection;
+ private WindowMagnificationManager mWindowMagnificationManager;
+ private MockContentResolver mMockResolver;
+ private MagnificationTransitionController mMagnificationTransitionController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ FakeSettingsProvider.clearSettingsProvider();
+ mMockResolver = new MockContentResolver();
+ mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ when(mContext.getContentResolver()).thenReturn(mMockResolver);
+ Settings.Secure.putFloatForUser(mMockResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, DEFAULT_SCALE,
+ CURRENT_USER_ID);
+ mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID);
+ when(mService.getFullScreenMagnificationController()).thenReturn(
+ mScreenMagnificationController);
+ when(mService.getWindowMagnificationMgr()).thenReturn(mWindowMagnificationManager);
+ mMockConnection = new MockWindowMagnificationConnection(true);
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationTransitionController = new MagnificationTransitionController(mService,
+ new Object());
+ }
+
+ @After
+ public void tearDown() {
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
+ @Test
+ public void transitionToWindowMode_notMagnifying_doNothing() throws RemoteException {
+ setMagnificationModeSettings(MODE_FULLSCREEN);
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW,
+ mTransitionCallBack);
+
+ verify(mTransitionCallBack).onResult(true);
+ verify(mScreenMagnificationController, never()).reset(anyInt(),
+ any(MagnificationAnimationCallback.class));
+ verify(mMockConnection.getConnection(), never()).enableWindowMagnification(anyInt(),
+ anyFloat(), anyFloat(), anyFloat(),
+ nullable(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
+ public void transitionToWindowMode_fullScreenMagnifying_disableFullScreenAndEnableWindow()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW,
+ mTransitionCallBack);
+
+ verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY),
+ mCallbackArgumentCaptor.capture());
+ mCallbackArgumentCaptor.getValue().onResult(true);
+ mMockConnection.invokeCallbacks();
+ verify(mTransitionCallBack).onResult(true);
+ assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ }
+
+ @Test
+ public void transitionToWindowMode_disablingWindowMode_enablingWindowWithFormerCenter()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW,
+ mTransitionCallBack);
+
+ mMockConnection.invokeCallbacks();
+ verify(mTransitionCallBack).onResult(true);
+ assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ }
+
+ @Test
+ public void transitionToFullScreenMode_windowMagnifying_disableWindowAndEnableFullScreen()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+ mMockConnection.invokeCallbacks();
+
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
+ DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
+ true, MAGNIFICATION_GESTURE_HANDLER_ID);
+ verify(mTransitionCallBack).onResult(true);
+ }
+
+ @Test
+ public void transitionToFullScreen_centerNotInTheBounds_magnifyTheCenterOfMagnificationBounds()
+ throws RemoteException {
+ final Rect magnificationBounds = MAGNIFICATION_REGION.getBounds();
+ final PointF magnifiedCenter = new PointF(magnificationBounds.right + 100,
+ magnificationBounds.bottom + 100);
+ setMagnificationEnabled(MODE_WINDOW, magnifiedCenter.x, magnifiedCenter.y);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+ mMockConnection.invokeCallbacks();
+
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE,
+ magnificationBounds.exactCenterX(), magnificationBounds.exactCenterY(), true,
+ MAGNIFICATION_GESTURE_HANDLER_ID);
+ verify(mTransitionCallBack).onResult(true);
+ }
+
+ @Test
+ public void transitionToFullScreenMode_disablingFullScreen_enableFullScreenWithFormerCenter()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW,
+ mTransitionCallBack);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+
+ verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
+ DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
+ true, MAGNIFICATION_GESTURE_HANDLER_ID);
+ verify(mTransitionCallBack).onResult(true);
+ }
+
+
+ @Test
+ public void interruptDuringTransitionToFullScreenMode_windowMagnifying_notifyTransitionFailed()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+
+ // Enable window magnification while animating.
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
+ Float.NaN, Float.NaN, null);
+ mMockConnection.invokeCallbacks();
+
+ assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ verify(mScreenMagnificationController, never()).setScaleAndCenter(TEST_DISPLAY,
+ DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
+ true, MAGNIFICATION_GESTURE_HANDLER_ID);
+ verify(mTransitionCallBack).onResult(false);
+ }
+
+ 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));
+
+ final boolean windowMagnifying = mWindowMagnificationManager.isWindowMagnifierEnabled(
+ TEST_DISPLAY);
+ if (windowMagnifying) {
+ mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMockConnection.invokeCallbacks();
+ }
+ if (mode == MODE_FULLSCREEN) {
+ when(mScreenMagnificationController.isMagnifying(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);
+ } else {
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
+ centerX, centerY, null);
+ mMockConnection.invokeCallbacks();
+ }
+ }
+
+ private void setMagnificationModeSettings(int mode) {
+ Settings.Secure.putIntForUser(mMockResolver,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, mode, CURRENT_USER_ID);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
index 10322e7ceff0..2a5350454f67 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
@@ -43,11 +44,23 @@ class MockWindowMagnificationConnection {
public static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
private final IWindowMagnificationConnection mConnection;
private final Binder mBinder;
+ private final boolean mSuspendCallback;
+ private boolean mHasPendingCallback = false;
+ private boolean mWindowMagnificationEnabled = false;
private IBinder.DeathRecipient mDeathRecipient;
private IWindowMagnificationConnectionCallback mIMirrorWindowCallback;
+
private Rect mMirrorWindowFrame = new Rect(0, 0, 500, 500);
+ private float mScale = 2.0f;
+ private Rect mSourceBounds = new Rect();
+ private IRemoteMagnificationAnimationCallback mAnimationCallback;
MockWindowMagnificationConnection() throws RemoteException {
+ this(false);
+ }
+
+ MockWindowMagnificationConnection(boolean suspendCallback) throws RemoteException {
+ mSuspendCallback = suspendCallback;
mConnection = mock(IWindowMagnificationConnection.class);
mBinder = mock(Binder.class);
when(mConnection.asBinder()).thenReturn(mBinder);
@@ -66,38 +79,57 @@ class MockWindowMagnificationConnection {
}
private void stubConnection() throws RemoteException {
+ stubEnableWindowMagnification();
+ stubDisableWindowMagnification();
+ }
+
+ private void stubEnableWindowMagnification() throws RemoteException {
doAnswer((invocation) -> {
final int displayId = invocation.getArgument(0);
if (displayId != TEST_DISPLAY) {
throw new IllegalArgumentException("only support default display :" + displayId);
}
+ mWindowMagnificationEnabled = true;
+ final float scale = invocation.getArgument(1);
+ mScale = Float.isNaN(scale) ? mScale : scale;
computeMirrorWindowFrame(invocation.getArgument(2), invocation.getArgument(3));
- final IRemoteMagnificationAnimationCallback callback = invocation.getArgument(4);
- if (callback != null) {
- callback.onResult(true);
- }
- if (mIMirrorWindowCallback != null) {
- mIMirrorWindowCallback.onWindowMagnifierBoundsChanged(TEST_DISPLAY,
- mMirrorWindowFrame);
+ setAnimationCallback(invocation.getArgument(4));
+ computeSourceBounds();
+ mHasPendingCallback = true;
+ if (!mSuspendCallback) {
+ invokeCallbacksInternal(true);
}
return null;
}).when(mConnection).enableWindowMagnification(anyInt(), anyFloat(), anyFloat(), anyFloat(),
nullable(IRemoteMagnificationAnimationCallback.class));
+ }
+ private void stubDisableWindowMagnification() throws RemoteException {
doAnswer((invocation) -> {
final int displayId = invocation.getArgument(0);
if (displayId != TEST_DISPLAY) {
throw new IllegalArgumentException("only support default display :" + displayId);
}
- final IRemoteMagnificationAnimationCallback callback = invocation.getArgument(1);
- if (callback != null) {
- callback.onResult(true);
+ setAnimationCallback(invocation.getArgument(1));
+ mHasPendingCallback = true;
+ if (!mSuspendCallback) {
+ invokeCallbacksInternal(true);
}
return null;
}).when(mConnection).disableWindowMagnification(anyInt(),
nullable(IRemoteMagnificationAnimationCallback.class));
}
+ private void computeSourceBounds() {
+ final int halfWidth = mMirrorWindowFrame.width() / 2;
+ final int halfHeight = mMirrorWindowFrame.height() / 2;
+ final int left = mMirrorWindowFrame.left + (halfWidth - (int) (halfWidth / mScale));
+ final int right = mMirrorWindowFrame.right - (halfWidth - (int) (halfWidth / mScale));
+ final int top = mMirrorWindowFrame.top + (halfHeight - (int) (halfHeight / mScale));
+ final int bottom = mMirrorWindowFrame.bottom - (halfHeight - (int) (halfHeight / mScale));
+ mSourceBounds.set(left, top, right, bottom);
+ }
+
private void computeMirrorWindowFrame(float centerX, float centerY) {
final float offsetX = Float.isNaN(centerX) ? 0
: centerX - mMirrorWindowFrame.exactCenterX();
@@ -106,6 +138,13 @@ class MockWindowMagnificationConnection {
mMirrorWindowFrame.offset((int) offsetX, (int) offsetY);
}
+ private void sendAnimationEndCallbackIfNeeded(boolean success) throws RemoteException {
+ if (mAnimationCallback != null) {
+ mAnimationCallback.onResult(success);
+ mAnimationCallback = null;
+ }
+ }
+
IWindowMagnificationConnection getConnection() {
return mConnection;
}
@@ -122,8 +161,38 @@ class MockWindowMagnificationConnection {
return mIMirrorWindowCallback;
}
- public Rect getMirrorWindowFrame() {
+ Rect getMirrorWindowFrame() {
return new Rect(mMirrorWindowFrame);
}
+
+ void invokeCallbacks() throws RemoteException {
+ if (!mSuspendCallback) {
+ throw new IllegalStateException("Invoke callbacks automatically");
+ }
+ invokeCallbacksInternal(true);
+ }
+
+ private void invokeCallbacksInternal(boolean success) throws RemoteException {
+ if (!mHasPendingCallback) {
+ throw new IllegalStateException("There is no any pending callbacks");
+ }
+ if (mWindowMagnificationEnabled && mIMirrorWindowCallback != null) {
+ mIMirrorWindowCallback.onWindowMagnifierBoundsChanged(TEST_DISPLAY,
+ mMirrorWindowFrame);
+ mIMirrorWindowCallback.onSourceBoundsChanged(TEST_DISPLAY,
+ mSourceBounds);
+ }
+ sendAnimationEndCallbackIfNeeded(success);
+ mHasPendingCallback = false;
+ }
+
+ private void setAnimationCallback(
+ @Nullable IRemoteMagnificationAnimationCallback animationCallback)
+ throws RemoteException {
+ if (mAnimationCallback != null) {
+ invokeCallbacksInternal(false);
+ }
+ mAnimationCallback = animationCallback;
+ }
}
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 9366e9535ab9..89b0a03a25bb 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
@@ -47,6 +47,7 @@ import android.test.mock.MockContentResolver;
import android.view.Display;
import android.view.InputDevice;
import android.view.MotionEvent;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
@@ -180,6 +181,8 @@ public class WindowMagnificationManagerTest {
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
mAnimationCallback);
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
+ eq(200f), eq(300f), any(IRemoteMagnificationAnimationCallback.class));
verify(mAnimationCallback).onResult(true);
}
@@ -195,13 +198,16 @@ public class WindowMagnificationManagerTest {
}
@Test
- public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification() {
+ public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification()
+ throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false,
+ mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false,
mAnimationCallback);
+ verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
+ any(IRemoteMagnificationAnimationCallback.class));
verify(mAnimationCallback).onResult(true);
}
@@ -354,6 +360,15 @@ public class WindowMagnificationManagerTest {
assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
}
+ @Test
+ public void centerGetter_enabledOnTestDisplay_expectedValues() {
+ mWindowMagnificationManager.requestConnection(true);
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+
+ assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
+ }
+
private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
final int len = pointersLocation.length;