From be41ea676c6bfa13d4a2b4cf3bc7f2048773243f Mon Sep 17 00:00:00 2001 From: mincheli Date: Wed, 22 Sep 2021 15:20:33 +0800 Subject: [PATCH] Notifies window magnifcation changes for AccessibilityService (1/n) See Design doc: go/b200769372 To make the behavior consistent on the new platform, The legcay callback, onMagnificationChanged(MagnificationController controller, Region region, float scale, float centerX, float centerY) keep notifying only full-screen magnification change as before. To support listening to the magnification changes of all magnification modes, the service should overide The new callback proposed in T, onMagnificationChanged(MagnificationController controller, Region region, MagnificationConfig config). TODO: Notify magnifcation change when window magnifier turns off Bug: 203013925 Test: atest AccessibilityMagnificationTest, atest MagnificationControllerTest, atest WindowMagnificationManagerTest, atest WindowMagnificationControllerTest, atest FullScreenMagnificationControllerTest, Change-Id: Ia73195a71f55bb26ef3f2eafce0dc73d8a65583b --- core/api/current.txt | 1 + .../AccessibilityService.java | 77 +++++++++++++------ .../IAccessibilityServiceClient.aidl | 3 +- core/java/android/app/UiAutomation.java | 3 +- .../WindowMagnificationController.java | 10 ++- .../WindowMagnificationControllerTest.java | 55 ++++++++++++- ...bstractAccessibilityServiceConnection.java | 22 +++--- .../AccessibilityManagerService.java | 21 +++-- .../FullScreenMagnificationController.java | 13 +++- .../MagnificationController.java | 11 +++ .../WindowMagnificationManager.java | 9 +++ ...FullScreenMagnificationControllerTest.java | 69 +++++++++++------ .../MagnificationControllerTest.java | 15 ++++ 13 files changed, 231 insertions(+), 78 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 98d3a91c99446..6e50c82161a66 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -3137,6 +3137,7 @@ package android.accessibilityservice { public static interface AccessibilityService.MagnificationController.OnMagnificationChangedListener { method public void onMagnificationChanged(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController, @NonNull android.graphics.Region, float, float, float); + method public default void onMagnificationChanged(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController, @NonNull android.graphics.Region, @NonNull android.accessibilityservice.MagnificationConfig); } public static final class AccessibilityService.ScreenshotResult { diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 3d38551642742..08230c550aeb6 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -587,7 +587,7 @@ public abstract class AccessibilityService extends Service { boolean onKeyEvent(KeyEvent event); /** Magnification changed callbacks for different displays */ void onMagnificationChanged(int displayId, @NonNull Region region, - float scale, float centerX, float centerY); + MagnificationConfig config); /** Callbacks for receiving motion events. */ void onMotionEvent(MotionEvent event); /** Callback for tuch state changes. */ @@ -1183,14 +1183,14 @@ public abstract class AccessibilityService extends Service { } } - private void onMagnificationChanged(int displayId, @NonNull Region region, float scale, - float centerX, float centerY) { + private void onMagnificationChanged(int displayId, @NonNull Region region, + MagnificationConfig config) { MagnificationController controller; synchronized (mLock) { controller = mMagnificationControllers.get(displayId); } if (controller != null) { - controller.dispatchMagnificationChanged(region, scale, centerX, centerY); + controller.dispatchMagnificationChanged(region, config); } } @@ -1328,8 +1328,8 @@ public abstract class AccessibilityService extends Service { * Dispatches magnification changes to any registered listeners. This * should be called on the service's main thread. */ - void dispatchMagnificationChanged(final @NonNull Region region, final float scale, - final float centerX, final float centerY) { + void dispatchMagnificationChanged(final @NonNull Region region, + final MagnificationConfig config) { final ArrayMap entries; synchronized (mLock) { if (mListeners == null || mListeners.isEmpty()) { @@ -1348,16 +1348,13 @@ public abstract class AccessibilityService extends Service { final OnMagnificationChangedListener listener = entries.keyAt(i); final Handler handler = entries.valueAt(i); if (handler != null) { - handler.post(new Runnable() { - @Override - public void run() { - listener.onMagnificationChanged(MagnificationController.this, - region, scale, centerX, centerY); - } + handler.post(() -> { + listener.onMagnificationChanged(MagnificationController.this, + region, config); }); } else { // We're already on the main thread, just run the listener. - listener.onMagnificationChanged(this, region, scale, centerX, centerY); + listener.onMagnificationChanged(this, region, config); } } } @@ -1665,6 +1662,10 @@ public abstract class AccessibilityService extends Service { public interface OnMagnificationChangedListener { /** * Called when the magnified region, scale, or center changes. + *

+ * Note: This legacy callback notifies only full-screen + * magnification change. + *

* * @param controller the magnification controller * @param region the magnification region @@ -1676,6 +1677,38 @@ public abstract class AccessibilityService extends Service { */ void onMagnificationChanged(@NonNull MagnificationController controller, @NonNull Region region, float scale, float centerX, float centerY); + + /** + * Called when the magnified region, mode, scale, or center changes of + * all magnification modes. + *

+ * Note: This method can be overridden to listen to the + * magnification changes of all magnification modes then the legacy callback + * would not receive the notifications. + * Skipping calling super when overriding this method results in + * {@link #onMagnificationChanged(MagnificationController, Region, float, float, float)} + * not getting called. + *

+ * + * @param controller the magnification controller + * @param region the magnification region + * If the config mode is + * {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}, + * it is the region of the screen currently active for magnification. + * that is the same region as {@link #getMagnificationRegion()}. + * If the config mode is + * {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}, + * it is the region of screen projected on the magnification window. + * @param config The magnification config. That has the controlling magnification + * mode, the new scale and the new screen-relative center position + */ + default void onMagnificationChanged(@NonNull MagnificationController controller, + @NonNull Region region, @NonNull MagnificationConfig config) { + if (config.getMode() == MAGNIFICATION_MODE_FULLSCREEN) { + onMagnificationChanged(controller, region, + config.getScale(), config.getCenterX(), config.getCenterY()); + } + } } } @@ -2370,9 +2403,8 @@ public abstract class AccessibilityService extends Service { @Override public void onMagnificationChanged(int displayId, @NonNull Region region, - float scale, float centerX, float centerY) { - AccessibilityService.this.onMagnificationChanged(displayId, region, scale, - centerX, centerY); + MagnificationConfig config) { + AccessibilityService.this.onMagnificationChanged(displayId, region, config); } @Override @@ -2496,12 +2528,10 @@ public abstract class AccessibilityService extends Service { /** Magnification changed callbacks for different displays */ public void onMagnificationChanged(int displayId, @NonNull Region region, - float scale, float centerX, float centerY) { + MagnificationConfig config) { final SomeArgs args = SomeArgs.obtain(); args.arg1 = region; - args.arg2 = scale; - args.arg3 = centerX; - args.arg4 = centerY; + args.arg2 = config; args.argi1 = displayId; final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args); @@ -2660,13 +2690,10 @@ public abstract class AccessibilityService extends Service { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { final SomeArgs args = (SomeArgs) message.obj; final Region region = (Region) args.arg1; - final float scale = (float) args.arg2; - final float centerX = (float) args.arg3; - final float centerY = (float) args.arg4; + final MagnificationConfig config = (MagnificationConfig) args.arg2; final int displayId = args.argi1; args.recycle(); - mCallback.onMagnificationChanged(displayId, region, scale, - centerX, centerY); + mCallback.onMagnificationChanged(displayId, region, config); } return; } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index 651c50f475c68..375383d5d8588 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -21,6 +21,7 @@ import android.graphics.Region; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityWindowInfo; import android.accessibilityservice.AccessibilityGestureEvent; +import android.accessibilityservice.MagnificationConfig; import android.view.KeyEvent; import android.view.MotionEvent; @@ -43,7 +44,7 @@ import android.view.MotionEvent; void onKeyEvent(in KeyEvent event, int sequence); - void onMagnificationChanged(int displayId, in Region region, float scale, float centerX, float centerY); + void onMagnificationChanged(int displayId, in Region region, in MagnificationConfig config); void onMotionEvent(in MotionEvent event); diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 58ded716cf40d..00903a8808340 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -22,6 +22,7 @@ import android.accessibilityservice.AccessibilityService.IAccessibilityServiceCl import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; import android.accessibilityservice.IAccessibilityServiceConnection; +import android.accessibilityservice.MagnificationConfig; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1581,7 +1582,7 @@ public final class UiAutomation { @Override public void onMagnificationChanged(int displayId, @NonNull Region region, - float scale, float centerX, float centerY) { + MagnificationConfig config) { /* do nothing */ } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index b064ba904120f..41b3e31dba9ac 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -252,7 +252,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mMagnificationFrame.height()); mTransaction.setGeometry(mMirrorSurface, mSourceBounds, mTmpRect, Surface.ROTATION_0).apply(); - mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId, mSourceBounds); + + // Notify source bounds change when the magnifier is not animating. + if (!mAnimationController.isAnimating()) { + mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId, + mSourceBounds); + } } }; mUpdateStateDescriptionRunnable = () -> { @@ -596,7 +601,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private void modifyWindowMagnification(SurfaceControl.Transaction t) { mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback); updateMirrorViewLayout(); - } /** @@ -800,7 +804,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold * are as same as current values, or the transition is interrupted * due to the new transition request. */ - void enableWindowMagnification(float scale, float centerX, float centerY, + public void enableWindowMagnification(float scale, float centerX, float centerY, float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, @Nullable IRemoteMagnificationAnimationCallback animationCallback) { mAnimationController.enableWindowMagnification(scale, centerX, centerY, diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index 8fdcaddc93fbd..5ad651728c66a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -34,6 +34,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.spy; @@ -60,6 +61,7 @@ import android.view.View; import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.IRemoteMagnificationAnimationCallback; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; @@ -75,6 +77,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @@ -89,8 +92,6 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Mock private Handler mHandler; @Mock - private WindowMagnificationAnimationController mWindowMagnificationAnimationController; - @Mock private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; @Mock private MirrorWindowControl mMirrorWindowControl; @@ -101,6 +102,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { private TestableWindowManager mWindowManager; private SysUiState mSysUiState = new SysUiState(); private Resources mResources; + private WindowMagnificationAnimationController mWindowMagnificationAnimationController; private WindowMagnificationController mWindowMagnificationController; private Instrumentation mInstrumentation; @@ -125,10 +127,11 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { return null; }).when(mHandler).post( any(Runnable.class)); - mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class)); mResources = getContext().getOrCreateTestableResources().getResources(); + mWindowMagnificationAnimationController = new WindowMagnificationAnimationController( + mContext); mWindowMagnificationController = new WindowMagnificationController(mContext, mHandler, mWindowMagnificationAnimationController, mSfVsyncFrameProvider, mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState); @@ -156,6 +159,52 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { eq(mContext.getDisplayId()), any(Rect.class)); } + @Test + public void enableWindowMagnification_notifySourceBoundsChanged() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN, /* magnificationFrameOffsetRatioX= */ 0, + /* magnificationFrameOffsetRatioY= */ 0, null); + }); + + // Waits for the surface created + verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onSourceBoundsChanged( + (eq(mContext.getDisplayId())), any()); + } + + @Test + public void enableWindowMagnification_withAnimation_schedulesFrame() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(2.0f, 10, + 10, /* magnificationFrameOffsetRatioX= */ 0, + /* magnificationFrameOffsetRatioY= */ 0, + Mockito.mock(IRemoteMagnificationAnimationCallback.class)); + }); + + verify(mSfVsyncFrameProvider, + timeout(LAYOUT_CHANGE_TIMEOUT_MS).atLeast(2)).postFrameCallback(any()); + } + + @Test + public void moveWindowMagnifier_enabled_notifySourceBoundsChanged() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN, 0, 0, null); + }); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.moveWindowMagnifier(10, 10); + }); + + final ArgumentCaptor sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class); + verify(mWindowMagnifierCallback, atLeast(2)).onSourceBoundsChanged( + (eq(mContext.getDisplayId())), sourceBoundsCaptor.capture()); + assertEquals(mWindowMagnificationController.getCenterX(), + sourceBoundsCaptor.getValue().exactCenterX(), 0); + assertEquals(mWindowMagnificationController.getCenterY(), + sourceBoundsCaptor.getValue().exactCenterY(), 0); + } + @Test public void enableWindowMagnification_systemGestureExclusionRectsIsSet() { mInstrumentation.runOnMainSync(() -> { diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index f050b6622a5d4..8e28da432fe58 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -1542,9 +1542,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region, - float scale, float centerX, float centerY) { + @NonNull MagnificationConfig config) { mInvocationHandler - .notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY); + .notifyMagnificationChangedLocked(displayId, region, config); } public void notifySoftKeyboardShowModeChangedLocked(int showState) { @@ -1564,15 +1564,15 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ * state of magnification has changed. */ private void notifyMagnificationChangedInternal(int displayId, @NonNull Region region, - float scale, float centerX, float centerY) { + @NonNull MagnificationConfig config) { final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); if (listener != null) { try { if (svcClientTracingEnabled()) { logTraceSvcClient("onMagnificationChanged", displayId + ", " + region + ", " - + scale + ", " + centerX + ", " + centerY); + + config.toString()); } - listener.onMagnificationChanged(displayId, region, scale, centerX, centerY); + listener.onMagnificationChanged(displayId, region, config); } catch (RemoteException re) { Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re); } @@ -1899,11 +1899,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ case MSG_ON_MAGNIFICATION_CHANGED: { final SomeArgs args = (SomeArgs) message.obj; final Region region = (Region) args.arg1; - final float scale = (float) args.arg2; - final float centerX = (float) args.arg3; - final float centerY = (float) args.arg4; + final MagnificationConfig config = (MagnificationConfig) args.arg2; final int displayId = args.argi1; - notifyMagnificationChangedInternal(displayId, region, scale, centerX, centerY); + notifyMagnificationChangedInternal(displayId, region, config); args.recycle(); } break; @@ -1932,7 +1930,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region, - float scale, float centerX, float centerY) { + @NonNull MagnificationConfig config) { synchronized (mLock) { if (mMagnificationCallbackState.get(displayId) == null) { return; @@ -1941,9 +1939,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final SomeArgs args = SomeArgs.obtain(); args.arg1 = region; - args.arg2 = scale; - args.arg3 = centerX; - args.arg4 = centerY; + args.arg2 = config; args.argi1 = displayId; final Message msg = obtainMessage(MSG_ON_MAGNIFICATION_CHANGED, args); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index e59a3d633cfd0..7074167a88c32 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -44,6 +44,7 @@ import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityShortcutInfo; import android.accessibilityservice.IAccessibilityServiceClient; +import android.accessibilityservice.MagnificationConfig; import android.accessibilityservice.TouchInteractionController; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1301,18 +1302,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Called by the MagnificationController when the state of display * magnification changes. * - * @param displayId The logical display id. + *

+ * It can notify window magnification change if the service supports controlling all the + * magnification mode. + *

+ * + * @param displayId The logical display id * @param region the new magnified region, may be empty if * magnification is not enabled (e.g. scale is 1) - * @param scale the new scale - * @param centerX the new screen-relative center X coordinate - * @param centerY the new screen-relative center Y coordinate + * @param config The magnification config. That has magnification mode, the new scale and the + * new screen-relative center position */ public void notifyMagnificationChanged(int displayId, @NonNull Region region, - float scale, float centerX, float centerY) { + @NonNull MagnificationConfig config) { synchronized (mLock) { notifyClearAccessibilityCacheLocked(); - notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY); + notifyMagnificationChangedLocked(displayId, region, config); } } @@ -1613,11 +1618,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region, - float scale, float centerX, float centerY) { + @NonNull MagnificationConfig config) { final AccessibilityUserState state = getCurrentUserStateLocked(); for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = state.mBoundServices.get(i); - service.notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY); + service.notifyMagnificationChangedLocked(displayId, region, config); } } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index 72bc8503b7a53..4d898c87f966f 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -17,10 +17,12 @@ package com.android.server.accessibility.magnification; import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL; +import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK; import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID; +import android.accessibilityservice.MagnificationConfig; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.NonNull; @@ -363,9 +365,16 @@ public class FullScreenMagnificationController implements return mIdOfLastServiceToMagnify; } + @GuardedBy("mLock") void onMagnificationChangedLocked() { - mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId, mMagnificationRegion, - getScale(), getCenterX(), getCenterY()); + final MagnificationConfig config = new MagnificationConfig.Builder() + .setMode(MAGNIFICATION_MODE_FULLSCREEN) + .setScale(getScale()) + .setCenterX(getCenterX()) + .setCenterY(getCenterY()).build(); + mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId, + mMagnificationRegion, + config); if (mUnregisterPending && !isMagnifying()) { unregister(mDeleteAfterUnregister); } 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 a63dd005f988e..1cf5c99c0d498 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -16,6 +16,7 @@ package com.android.server.accessibility.magnification; +import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_WINDOW; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE; @@ -379,6 +380,16 @@ public class MagnificationController implements WindowMagnificationManager.Callb mAms.changeMagnificationMode(displayId, magnificationMode); } + @Override + public void onSourceBoundsChanged(int displayId, Rect bounds) { + final MagnificationConfig config = new MagnificationConfig.Builder() + .setMode(MAGNIFICATION_MODE_WINDOW) + .setScale(mScaleProvider.getScale(displayId)) + .setCenterX(bounds.exactCenterX()) + .setCenterY(bounds.exactCenterY()).build(); + mAms.notifyMagnificationChanged(displayId, new Region(bounds), config); + } + private void disableFullScreenMagnificationIfNeeded(int displayId) { final FullScreenMagnificationController fullScreenMagnificationController = getFullScreenMagnificationController(); 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 336f0bbbe5185..94ce41f089dde 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -138,6 +138,14 @@ public class WindowMagnificationManager implements */ void onWindowMagnificationActivationState(int displayId, boolean activated); + /** + * Called when the magnification source bounds are changed. + * + * @param displayId The logical display id. + * @param bounds The magnified source bounds on the display. + */ + void onSourceBoundsChanged(int displayId, Rect bounds); + /** * Called from {@link IWindowMagnificationConnection} to request changing the magnification * mode on the given display. @@ -688,6 +696,7 @@ public class WindowMagnificationManager implements } magnifier.onSourceBoundsChanged(sourceBounds); } + mCallback.onSourceBoundsChanged(displayId, sourceBounds); } @Override diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index 96af61737bff0..dadc890c56e4d 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -16,6 +16,8 @@ package com.android.server.accessibility.magnification; +import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; + import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback; import static org.junit.Assert.assertEquals; @@ -23,10 +25,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; 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.Matchers.anyObject; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -37,6 +37,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.mockito.hamcrest.MockitoHamcrest.argThat; +import android.accessibilityservice.MagnificationConfig; import android.animation.ValueAnimator; import android.content.BroadcastReceiver; import android.content.Context; @@ -105,6 +106,8 @@ public class FullScreenMagnificationControllerTest { private final MagnificationScaleProvider mScaleProvider = mock( MagnificationScaleProvider.class); + private final ArgumentCaptor mConfigCaptor = ArgumentCaptor.forClass( + MagnificationConfig.class); ValueAnimator mMockValueAnimator; ValueAnimator.AnimatorUpdateListener mTargetAnimationListener; @@ -142,13 +145,13 @@ public class FullScreenMagnificationControllerTest { register(DISPLAY_1); register(INVALID_DISPLAY); verify(mMockContext).registerReceiver( - (BroadcastReceiver) anyObject(), (IntentFilter) anyObject()); + any(BroadcastReceiver.class), any(IntentFilter.class)); verify(mMockWindowManager).setMagnificationCallbacks( - eq(DISPLAY_0), (MagnificationCallbacks) anyObject()); + eq(DISPLAY_0), any(MagnificationCallbacks.class)); verify(mMockWindowManager).setMagnificationCallbacks( - eq(DISPLAY_1), (MagnificationCallbacks) anyObject()); + eq(DISPLAY_1), any(MagnificationCallbacks.class)); verify(mMockWindowManager).setMagnificationCallbacks( - eq(INVALID_DISPLAY), (MagnificationCallbacks) anyObject()); + eq(INVALID_DISPLAY), any(MagnificationCallbacks.class)); assertTrue(mFullScreenMagnificationController.isRegistered(DISPLAY_0)); assertTrue(mFullScreenMagnificationController.isRegistered(DISPLAY_1)); assertFalse(mFullScreenMagnificationController.isRegistered(INVALID_DISPLAY)); @@ -159,9 +162,9 @@ public class FullScreenMagnificationControllerTest { register(DISPLAY_0); register(DISPLAY_1); mFullScreenMagnificationController.unregister(DISPLAY_0); - verify(mMockContext, times(0)).unregisterReceiver((BroadcastReceiver) anyObject()); + verify(mMockContext, times(0)).unregisterReceiver(any(BroadcastReceiver.class)); mFullScreenMagnificationController.unregister(DISPLAY_1); - verify(mMockContext).unregisterReceiver((BroadcastReceiver) anyObject()); + verify(mMockContext).unregisterReceiver(any(BroadcastReceiver.class)); verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_0), eq(null)); verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_1), eq(null)); assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_0)); @@ -343,6 +346,7 @@ public class FullScreenMagnificationControllerTest { MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); float scale = 2.5f; PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + final MagnificationConfig config = buildConfig(scale, newCenter.x, newCenter.y); PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); MagnificationSpec endSpec = getMagnificationSpec(scale, offsets); @@ -353,8 +357,9 @@ public class FullScreenMagnificationControllerTest { assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5); assertEquals(newCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.5); assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec)); - verify(mMockAms).notifyMagnificationChanged(displayId, - INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y); + verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(INITIAL_MAGNIFICATION_REGION), + mConfigCaptor.capture()); + assertConfigEquals(config, mConfigCaptor.getValue()); verify(mMockValueAnimator).start(); verify(mRequestObserver).onRequestMagnificationSpec(displayId, SERVICE_ID_1); @@ -494,8 +499,11 @@ public class FullScreenMagnificationControllerTest { MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); callbacks.onMagnificationRegionChanged(OTHER_REGION); mMessageCapturingHandler.sendAllMessages(); - verify(mMockAms).notifyMagnificationChanged(displayId, OTHER_REGION, 1.0f, - OTHER_MAGNIFICATION_BOUNDS.centerX(), OTHER_MAGNIFICATION_BOUNDS.centerY()); + MagnificationConfig config = buildConfig(1.0f, OTHER_MAGNIFICATION_BOUNDS.centerX(), + OTHER_MAGNIFICATION_BOUNDS.centerY()); + verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(OTHER_REGION), + mConfigCaptor.capture()); + assertConfigEquals(config, mConfigCaptor.getValue()); } @Test @@ -650,7 +658,7 @@ public class FullScreenMagnificationControllerTest { reset(mMockAms); assertTrue(mFullScreenMagnificationController.resetIfNeeded(displayId, false)); verify(mMockAms).notifyMagnificationChanged(eq(displayId), - eq(INITIAL_MAGNIFICATION_REGION), eq(1.0f), anyFloat(), anyFloat()); + eq(INITIAL_MAGNIFICATION_REGION), any(MagnificationConfig.class)); assertFalse(mFullScreenMagnificationController.isMagnifying(displayId)); assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, false)); } @@ -668,8 +676,8 @@ public class FullScreenMagnificationControllerTest { assertFalse(mFullScreenMagnificationController.reset(displayId, mAnimationCallback)); mMessageCapturingHandler.sendAllMessages(); - verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId), - any(Region.class), anyFloat(), anyFloat(), anyFloat()); + verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId), any(Region.class), + any(MagnificationConfig.class)); verify(mAnimationCallback).onResult(true); } @@ -726,7 +734,7 @@ public class FullScreenMagnificationControllerTest { ArgumentCaptor broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); verify(mMockContext).registerReceiver( - broadcastReceiverCaptor.capture(), (IntentFilter) anyObject()); + broadcastReceiverCaptor.capture(), any(IntentFilter.class)); BroadcastReceiver br = broadcastReceiverCaptor.getValue(); zoomIn2xToMiddle(DISPLAY_0); zoomIn2xToMiddle(DISPLAY_1); @@ -1031,6 +1039,7 @@ public class FullScreenMagnificationControllerTest { MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); float scale = 2.5f; PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + final MagnificationConfig config = buildConfig(scale, firstCenter.x, firstCenter.y); MagnificationSpec firstEndSpec = getMagnificationSpec( scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, firstCenter, scale)); @@ -1047,8 +1056,9 @@ public class FullScreenMagnificationControllerTest { when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec)); - verify(mMockAms).notifyMagnificationChanged(displayId, - INITIAL_MAGNIFICATION_REGION, scale, firstCenter.x, firstCenter.y); + verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(INITIAL_MAGNIFICATION_REGION), + mConfigCaptor.capture()); + assertConfigEquals(config, mConfigCaptor.getValue()); Mockito.reset(mMockWindowManager); // Intermediate point @@ -1062,6 +1072,7 @@ public class FullScreenMagnificationControllerTest { Mockito.reset(mMockWindowManager); PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER; + final MagnificationConfig newConfig = buildConfig(scale, newCenter.x, newCenter.y); MagnificationSpec newEndSpec = getMagnificationSpec( scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale)); assertTrue(mFullScreenMagnificationController.setCenter(displayId, @@ -1070,8 +1081,9 @@ public class FullScreenMagnificationControllerTest { // Animation should have been restarted verify(mMockValueAnimator, times(2)).start(); - verify(mMockAms).notifyMagnificationChanged(displayId, - INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y); + verify(mMockAms, times(2)).notifyMagnificationChanged(eq(displayId), + eq(INITIAL_MAGNIFICATION_REGION), mConfigCaptor.capture()); + assertConfigEquals(newConfig, mConfigCaptor.getValue()); // New starting point should be where we left off when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f); @@ -1155,7 +1167,7 @@ public class FullScreenMagnificationControllerTest { Region regionArg = (Region) args[1]; regionArg.set(INITIAL_MAGNIFICATION_REGION); return null; - }).when(mMockWindowManager).getMagnificationRegion(anyInt(), (Region) anyObject()); + }).when(mMockWindowManager).getMagnificationRegion(anyInt(), any(Region.class)); } private void resetMockWindowManager() { @@ -1201,6 +1213,19 @@ public class FullScreenMagnificationControllerTest { magnifiedBounds.centerY() - scale * center.y); } + private MagnificationConfig buildConfig(float scale, float centerX, float centerY) { + return new MagnificationConfig.Builder().setMode( + MAGNIFICATION_MODE_FULLSCREEN).setScale(scale).setCenterX(centerX).setCenterY( + centerY).build(); + } + + private void assertConfigEquals(MagnificationConfig expected, MagnificationConfig result) { + assertEquals(expected.getMode(), result.getMode()); + assertEquals(expected.getScale(), result.getScale(), 0f); + assertEquals(expected.getCenterX(), result.getCenterX(), 0f); + assertEquals(expected.getCenterY(), result.getCenterY(), 0f); + } + private MagnificationSpec getInterpolatedMagSpec(MagnificationSpec start, MagnificationSpec end, float fraction) { MagnificationSpec interpolatedSpec = new MagnificationSpec(); 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 c9ae11a14ed30..580488be0436f 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 @@ -428,6 +428,21 @@ public class MagnificationControllerTest { verify(mWindowMagnificationManager).persistScale(eq(TEST_DISPLAY)); } + @Test + public void onSourceBoundsChanged_notifyMagnificationChanged() { + Rect rect = new Rect(0, 0, 100, 120); + Region region = new Region(rect); + + mMagnificationController.onSourceBoundsChanged(TEST_DISPLAY, rect); + + final ArgumentCaptor configCaptor = ArgumentCaptor.forClass( + MagnificationConfig.class); + verify(mService).notifyMagnificationChanged(eq(TEST_DISPLAY), eq(region), + configCaptor.capture()); + assertEquals(rect.exactCenterX(), configCaptor.getValue().getCenterX(), 0); + assertEquals(rect.exactCenterY(), configCaptor.getValue().getCenterY(), 0); + } + @Test public void onAccessibilityActionPerformed_magnifierEnabled_showMagnificationButton() throws RemoteException {