From 0dae9e4f47422a0ced0eb018be65ed566ae5b978 Mon Sep 17 00:00:00 2001 From: Daniel Hsieh Date: Tue, 15 Feb 2022 15:14:02 +0000 Subject: [PATCH 1/2] Adds moveWindowMagnifierToPosition method For following typing focus feature, we add a moveWindowMagnifierToPosition method for a window magnifier to move it to the center of onRectangleOnScreenRequested. Apart from calling this method to fulfill movement functionality, we can separate the movement from calling enableWindowMagnification which is a temporary replacement for movement functionality. Therefore, we can distinguish whether the it is enabled by the first time or the further movement behavior. It would help us differentiate the condition whether we should reset the following typing focus to on/off state. (b/216394179) Bug: 219654280 Test: atest IWindowMagnificationConnectionTest atest WindowMagnificationTest atest WindowMagnificationAnimationControllerTest atest WindowMagnificationControllerTest atest WindowMagnificationConnectionWrapperTest atest WindowMagnificationManagerTest Change-Id: Iaac3ca6868d948571c1aee54a5f122c9697059d8 --- .../IWindowMagnificationConnection.aidl | 22 ++- .../accessibility/WindowMagnification.java | 11 ++ ...indowMagnificationAnimationController.java | 39 ++++- .../WindowMagnificationConnectionImpl.java | 7 + .../WindowMagnificationController.java | 8 + .../IWindowMagnificationConnectionTest.java | 10 ++ .../MockMagnificationAnimationCallback.java | 54 +++++++ ...wMagnificationAnimationControllerTest.java | 142 ++++++++++++++++++ .../WindowMagnificationControllerTest.java | 82 ++++++++++ .../WindowMagnificationConnectionWrapper.java | 19 +++ .../WindowMagnificationManager.java | 29 ++-- ...dowMagnificationConnectionWrapperTest.java | 8 + .../WindowMagnificationManagerTest.java | 29 ++-- 13 files changed, 425 insertions(+), 35 deletions(-) create mode 100644 packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl index 67d96678f420d..62d029bd1be62 100644 --- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl +++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl @@ -32,7 +32,7 @@ oneway interface IWindowMagnificationConnection { /** * Enables window magnification on specified display with given center and scale and animation. * - * @param displayId The logical display id. + * @param displayId the logical display id. * @param scale magnification scale. * @param centerX the screen-relative X coordinate around which to center, * or {@link Float#NaN} to leave unchanged. @@ -51,7 +51,7 @@ oneway interface IWindowMagnificationConnection { /** * Sets the scale of the window magnifier on specified display. * - * @param displayId The logical display id. + * @param displayId the logical display id. * @param scale magnification scale. */ void setScale(int displayId, float scale); @@ -59,7 +59,7 @@ oneway interface IWindowMagnificationConnection { /** * Disables window magnification on specified display with animation. * - * @param displayId The logical display id. + * @param displayId the logical display id. * @param callback The callback called when the animation is completed or interrupted. */ void disableWindowMagnification(int displayId, @@ -68,6 +68,7 @@ oneway interface IWindowMagnificationConnection { /** * Moves the window magnifier on the specified display. It has no effect while animating. * + * @param displayId the logical display id. * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in * current screen pixels. * @param offsetY the amount in pixels to offset the window magnifier in the Y direction, in @@ -75,10 +76,21 @@ oneway interface IWindowMagnificationConnection { */ void moveWindowMagnifier(int displayId, float offsetX, float offsetY); + /** + * Moves the window magnifier on the given display. + * + * @param displayId the logical display id. + * @param positionX the x-axis position of the center of the magnified source bounds. + * @param positionY the y-axis position of the center of the magnified source bounds. + * @param callback the callback called when the animation is completed or interrupted. + */ + void moveWindowMagnifierToPosition(int displayId, float positionX, float positionY, + in IRemoteMagnificationAnimationCallback callback); + /** * Requests System UI show magnification mode button UI on the specified display. * - * @param displayId The logical display id. + * @param displayId the logical display id. * @param magnificationMode the current magnification mode. */ void showMagnificationButton(int displayId, int magnificationMode); @@ -86,7 +98,7 @@ oneway interface IWindowMagnificationConnection { /** * Requests System UI remove magnification mode button UI on the specified display. * - * @param displayId The logical display id. + * @param displayId the logical display id. */ void removeMagnificationButton(int displayId); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java index 885a1777f30b2..9b7a8f8d915ca 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java @@ -171,6 +171,17 @@ public class WindowMagnification extends CoreStartable implements WindowMagnifie } } + @MainThread + void moveWindowMagnifierToPositionInternal(int displayId, float positionX, float positionY, + IRemoteMagnificationAnimationCallback callback) { + final WindowMagnificationController windowMagnificationController = + mMagnificationControllerSupplier.get(displayId); + if (windowMagnificationController != null) { + windowMagnificationController.moveWindowMagnifierToPosition(positionX, positionY, + callback); + } + } + @MainThread void disableWindowMagnification(int displayId, @Nullable IRemoteMagnificationAnimationCallback callback) { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java index dc1e0054ff24b..3b4114bfb9844 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java @@ -156,6 +156,7 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp } mAnimationCallback = animationCallback; setupEnableAnimationSpecs(scale, centerX, centerY); + if (mEndSpec.equals(mStartSpec)) { if (mState == STATE_DISABLED) { mController.enableWindowMagnificationInternal(scale, centerX, centerY, @@ -178,6 +179,24 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp } } + void moveWindowMagnifierToPosition(float centerX, float centerY, + IRemoteMagnificationAnimationCallback callback) { + if (mState == STATE_ENABLED) { + // We set the animation duration to shortAnimTime which would be reset at the end. + mValueAnimator.setDuration(mContext.getResources() + .getInteger(com.android.internal.R.integer.config_shortAnimTime)); + enableWindowMagnification(Float.NaN, centerX, centerY, + /* magnificationFrameOffsetRatioX */ Float.NaN, + /* magnificationFrameOffsetRatioY */ Float.NaN, callback); + } else if (mState == STATE_ENABLING) { + sendAnimationCallback(false); + mAnimationCallback = callback; + mValueAnimator.setDuration(mContext.getResources() + .getInteger(com.android.internal.R.integer.config_shortAnimTime)); + setupEnableAnimationSpecs(Float.NaN, centerX, centerY); + } + } + private void setupEnableAnimationSpecs(float scale, float centerX, float centerY) { if (mController == null) { return; @@ -193,9 +212,16 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp R.integer.magnification_default_scale) : scale, centerX, centerY); } else { mStartSpec.set(currentScale, currentCenterX, currentCenterY); - mEndSpec.set(Float.isNaN(scale) ? currentScale : scale, - Float.isNaN(centerX) ? currentCenterX : centerX, - Float.isNaN(centerY) ? currentCenterY : centerY); + + final float endScale = (mState == STATE_ENABLING ? mEndSpec.mScale : currentScale); + final float endCenterX = + (mState == STATE_ENABLING ? mEndSpec.mCenterX : currentCenterX); + final float endCenterY = + (mState == STATE_ENABLING ? mEndSpec.mCenterY : currentCenterY); + + mEndSpec.set(Float.isNaN(scale) ? endScale : scale, + Float.isNaN(centerX) ? endCenterX : centerX, + Float.isNaN(centerY) ? endCenterY : centerY); } if (DEBUG) { Log.d(TAG, "SetupEnableAnimationSpecs : mStartSpec = " + mStartSpec + ", endSpec = " @@ -269,6 +295,9 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp setState(STATE_ENABLED); } sendAnimationCallback(true); + // We reset the duration to config_longAnimTime + mValueAnimator.setDuration(mContext.getResources() + .getInteger(com.android.internal.R.integer.config_longAnimTime)); } @Override @@ -313,10 +342,10 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY); } - private static ValueAnimator newValueAnimator(Resources resources) { + private static ValueAnimator newValueAnimator(Resources resource) { final ValueAnimator valueAnimator = new ValueAnimator(); valueAnimator.setDuration( - resources.getInteger(com.android.internal.R.integer.config_longAnimTime)); + resource.getInteger(com.android.internal.R.integer.config_longAnimTime)); valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f)); valueAnimator.setFloatValues(0.0f, 1.0f); return valueAnimator; diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java index 1d22633455e96..0522d43e99fb9 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java @@ -76,6 +76,13 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S () -> mWindowMagnification.moveWindowMagnifier(displayId, offsetX, offsetY)); } + @Override + public void moveWindowMagnifierToPosition(int displayId, float positionX, float positionY, + IRemoteMagnificationAnimationCallback callback) { + mHandler.post(() -> mWindowMagnification.moveWindowMagnifierToPositionInternal( + displayId, positionX, positionY, callback)); + } + @Override public void showMagnificationButton(int displayId, int magnificationMode) { mHandler.post( diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index de03993a6f17e..e109b5c9e519b 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -985,6 +985,14 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } } + void moveWindowMagnifierToPosition(float positionX, float positionY, + IRemoteMagnificationAnimationCallback callback) { + if (mMirrorSurfaceView == null) { + return; + } + mAnimationController.moveWindowMagnifierToPosition(positionX, positionY, callback); + } + /** * Gets the scale. * diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java index 796af115bf681..58b4af43a9b7b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java @@ -133,6 +133,16 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase { verify(mWindowMagnificationController).moveWindowMagnifier(100f, 200f); } + @Test + public void moveWindowMagnifierToPosition() throws RemoteException { + mIWindowMagnificationConnection.moveWindowMagnifierToPosition(TEST_DISPLAY, + 100f, 200f, mAnimationCallback); + waitForIdleSync(); + + verify(mWindowMagnificationController).moveWindowMagnifierToPosition( + eq(100f), eq(200f), any(IRemoteMagnificationAnimationCallback.class)); + } + @Test public void showMagnificationButton() throws RemoteException { mIWindowMagnificationConnection.showMagnificationButton(TEST_DISPLAY, diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java new file mode 100644 index 0000000000000..30bff0943da78 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 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.systemui.accessibility; + +import android.os.RemoteException; +import android.view.accessibility.IRemoteMagnificationAnimationCallback; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; + +public class MockMagnificationAnimationCallback extends IRemoteMagnificationAnimationCallback.Stub { + + private final CountDownLatch mCountDownLatch; + private final AtomicInteger mSuccessCount; + private final AtomicInteger mFailedCount; + + MockMagnificationAnimationCallback(CountDownLatch countDownLatch) { + mCountDownLatch = countDownLatch; + mSuccessCount = new AtomicInteger(); + mFailedCount = new AtomicInteger(); + } + + public int getSuccessCount() { + return mSuccessCount.get(); + } + + public int getFailedCount() { + return mFailedCount.get(); + } + + @Override + public void onResult(boolean success) throws RemoteException { + mCountDownLatch.countDown(); + if (success) { + mSuccessCount.getAndIncrement(); + } else { + mFailedCount.getAndIncrement(); + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java index 3cc177dd8d91d..21c3d6ea0660a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java @@ -60,6 +60,8 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @Ignore @@ -217,6 +219,29 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { verify(mAnimationCallback2).onResult(true); } + @Test + public void enableWindowMagnificationWithUnchanged_enabling_expectedValuesToDefault() + throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(2); + final MockMagnificationAnimationCallback animationCallback = + new MockMagnificationAnimationCallback(countDownLatch); + + enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + animationCallback); + mInstrumentation.runOnMainSync( + () -> { + mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN, + Float.NaN, Float.NaN, animationCallback); + }); + + assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); + // The callback in 2nd enableWindowMagnification will return true + assertEquals(1, animationCallback.getSuccessCount()); + // The callback in 1st enableWindowMagnification will return false + assertEquals(1, animationCallback.getFailedCount()); + verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); + } + @Test public void enableWindowMagnificationWithScaleOne_enabled_AnimationAndInvokeCallback() throws RemoteException { @@ -424,6 +449,102 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { Math.round(offsetRatio * mirrorViewBound.height() / 2), 0.1f); } + @Test + public void moveWindowMagnifierToPosition_enabled_expectedValues() + throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + final MockMagnificationAnimationCallback animationCallback = + new MockMagnificationAnimationCallback(countDownLatch); + final float targetCenterX = DEFAULT_CENTER_X + 100; + final float targetCenterY = DEFAULT_CENTER_Y + 100; + enableWindowMagnificationWithoutAnimation(); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + targetCenterX, targetCenterY, animationCallback); + }); + + assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); + assertEquals(1, animationCallback.getSuccessCount()); + assertEquals(0, animationCallback.getFailedCount()); + verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY); + } + + @Test + public void moveWindowMagnifierToPositionMultipleTimes_enabled_expectedValuesToLastOne() + throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(4); + final MockMagnificationAnimationCallback animationCallback = + new MockMagnificationAnimationCallback(countDownLatch); + enableWindowMagnificationWithoutAnimation(); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + DEFAULT_CENTER_X + 10, DEFAULT_CENTER_Y + 10, animationCallback); + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + DEFAULT_CENTER_X + 20, DEFAULT_CENTER_Y + 20, animationCallback); + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + DEFAULT_CENTER_X + 30, DEFAULT_CENTER_Y + 30, animationCallback); + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40, animationCallback); + }); + + assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); + // only the last one callback will return true + assertEquals(1, animationCallback.getSuccessCount()); + // the others will return false + assertEquals(3, animationCallback.getFailedCount()); + verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40); + } + + @Test + public void moveWindowMagnifierToPosition_enabling_expectedValuesToLastOne() + throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(2); + final MockMagnificationAnimationCallback animationCallback = + new MockMagnificationAnimationCallback(countDownLatch); + final float targetCenterX = DEFAULT_CENTER_X + 100; + final float targetCenterY = DEFAULT_CENTER_Y + 100; + + enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + animationCallback); + mInstrumentation.runOnMainSync( + () -> { + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + targetCenterX, targetCenterY, animationCallback); + }); + + assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); + // The callback in moveWindowMagnifierToPosition will return true + assertEquals(1, animationCallback.getSuccessCount()); + // The callback in enableWindowMagnification will return false + assertEquals(1, animationCallback.getFailedCount()); + verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY); + } + + @Test + public void moveWindowMagnifierToPositionWithCenterUnchanged_enabling_expectedValuesToDefault() + throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(2); + final MockMagnificationAnimationCallback animationCallback = + new MockMagnificationAnimationCallback(countDownLatch); + + enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + animationCallback); + mInstrumentation.runOnMainSync( + () -> { + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + Float.NaN, Float.NaN, animationCallback); + }); + + assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); + // The callback in moveWindowMagnifierToPosition will return true + assertEquals(1, animationCallback.getSuccessCount()); + // The callback in enableWindowMagnification will return false + assertEquals(1, animationCallback.getFailedCount()); + verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); + } + @Test public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback() throws RemoteException { @@ -569,6 +690,20 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 100f, DEFAULT_CENTER_Y + 100f); } + @Test + public void moveWindowMagnifierToPosition_enabled() { + final float targetCenterX = DEFAULT_CENTER_X + 100; + final float targetCenterY = DEFAULT_CENTER_Y + 100; + enableWindowMagnificationWithoutAnimation(); + + mInstrumentation.runOnMainSync( + () -> mController.moveWindowMagnifierToPosition(targetCenterX, targetCenterY, + mAnimationCallback)); + SystemClock.sleep(mWaitingAnimationPeriod); + + verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY); + } + private void verifyFinalSpec(float expectedScale, float expectedCenterX, float expectedCenterY) { assertEquals(expectedScale, mController.getScale(), 0f); @@ -662,6 +797,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { mSpyController.moveWindowMagnifier(offsetX, offsetY); } + @Override + void moveWindowMagnifierToPosition(float positionX, float positionY, + IRemoteMagnificationAnimationCallback callback) { + super.moveWindowMagnifierToPosition(positionX, positionY, callback); + mSpyController.moveWindowMagnifierToPosition(positionX, positionY, callback); + } + @Override void setScale(float scale) { super.setScale(scale); 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 6e5926db519d9..19efd11d3a008 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -88,6 +88,8 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @LargeTest @@ -96,12 +98,16 @@ import java.util.concurrent.atomic.AtomicInteger; public class WindowMagnificationControllerTest extends SysuiTestCase { private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000; + private static final long ANIMATION_DURATION_MS = 300; + private final long mWaitingAnimationPeriod = 2 * ANIMATION_DURATION_MS; @Mock private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; @Mock private MirrorWindowControl mMirrorWindowControl; @Mock private WindowMagnifierCallback mWindowMagnifierCallback; + @Mock + IRemoteMagnificationAnimationCallback mAnimationCallback; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); @@ -286,6 +292,82 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { verify(mSfVsyncFrameProvider, atLeastOnce()).postFrameCallback(any()); } + @Test + public void moveWindowMagnifierToPositionWithAnimation_expectedValuesAndInvokeCallback() + throws InterruptedException { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN, 0, 0, null); + }); + final CountDownLatch countDownLatch = new CountDownLatch(1); + final MockMagnificationAnimationCallback animationCallback = + new MockMagnificationAnimationCallback(countDownLatch); + final ArgumentCaptor sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class); + verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)) + .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture()); + final float targetCenterX = sourceBoundsCaptor.getValue().exactCenterX() + 10; + final float targetCenterY = sourceBoundsCaptor.getValue().exactCenterY() + 10; + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.moveWindowMagnifierToPosition( + targetCenterX, targetCenterY, animationCallback); + }); + + assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); + assertEquals(1, animationCallback.getSuccessCount()); + assertEquals(0, animationCallback.getFailedCount()); + verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)) + .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture()); + assertEquals(mWindowMagnificationController.getCenterX(), + sourceBoundsCaptor.getValue().exactCenterX(), 0); + assertEquals(mWindowMagnificationController.getCenterY(), + sourceBoundsCaptor.getValue().exactCenterY(), 0); + assertEquals(mWindowMagnificationController.getCenterX(), targetCenterX, 0); + assertEquals(mWindowMagnificationController.getCenterY(), targetCenterY, 0); + } + + @Test + public void moveWindowMagnifierToPositionMultipleTimes_expectedValuesAndInvokeCallback() + throws InterruptedException { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN, 0, 0, null); + }); + final CountDownLatch countDownLatch = new CountDownLatch(4); + final MockMagnificationAnimationCallback animationCallback = + new MockMagnificationAnimationCallback(countDownLatch); + final ArgumentCaptor sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class); + verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)) + .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture()); + final float centerX = sourceBoundsCaptor.getValue().exactCenterX(); + final float centerY = sourceBoundsCaptor.getValue().exactCenterY(); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.moveWindowMagnifierToPosition( + centerX + 10, centerY + 10, animationCallback); + mWindowMagnificationController.moveWindowMagnifierToPosition( + centerX + 20, centerY + 20, animationCallback); + mWindowMagnificationController.moveWindowMagnifierToPosition( + centerX + 30, centerY + 30, animationCallback); + mWindowMagnificationController.moveWindowMagnifierToPosition( + centerX + 40, centerY + 40, animationCallback); + }); + + assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); + // only the last one callback will return true + assertEquals(1, animationCallback.getSuccessCount()); + // the others will return false + assertEquals(3, animationCallback.getFailedCount()); + verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)) + .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture()); + assertEquals(mWindowMagnificationController.getCenterX(), + sourceBoundsCaptor.getValue().exactCenterX(), 0); + assertEquals(mWindowMagnificationController.getCenterY(), + sourceBoundsCaptor.getValue().exactCenterY(), 0); + assertEquals(mWindowMagnificationController.getCenterX(), centerX + 40, 0); + assertEquals(mWindowMagnificationController.getCenterY(), centerY + 40, 0); + } + @Test public void setScale_enabled_expectedValueAndUpdateStateDescription() { mInstrumentation.runOnMainSync( diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java index 25dcc2aea41b8..041eece5ce488 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java @@ -133,6 +133,25 @@ class WindowMagnificationConnectionWrapper { return true; } + boolean moveWindowMagnifierToPosition(int displayId, float positionX, float positionY, + @Nullable MagnificationAnimationCallback callback) { + if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) { + mTrace.logTrace(TAG + ".moveWindowMagnifierToPosition", + FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "displayId=" + displayId + + ";positionX=" + positionX + ";positionY=" + positionY); + } + try { + mConnection.moveWindowMagnifierToPosition(displayId, positionX, positionY, + transformToRemoteCallback(callback, mTrace)); + } catch (RemoteException e) { + if (DBG) { + Slog.e(TAG, "Error calling moveWindowMagnifierToPosition()", e); + } + return false; + } + return true; + } + boolean showMagnificationButton(int displayId, int magnificationMode) { if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) { mTrace.logTrace(TAG + ".showMagnificationButton", 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 89910eac06c50..f38dcf18db5cc 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -314,9 +314,12 @@ public class WindowMagnificationManager implements float toCenterX = (float) (left + right) / 2; float toCenterY = (float) (top + bottom) / 2; - if (!isPositionInSourceBounds(displayId, toCenterX, toCenterY) - && isTrackingTypingFocusEnabled(displayId)) { - enableWindowMagnification(displayId, Float.NaN, toCenterX, toCenterY); + synchronized (mLock) { + if (!isPositionInSourceBounds(displayId, toCenterX, toCenterY) + && isTrackingTypingFocusEnabled(displayId)) { + moveWindowMagnifierToPositionInternal(displayId, toCenterX, toCenterY, + STUB_ANIMATION_CALLBACK); + } } } @@ -563,14 +566,13 @@ public class WindowMagnificationManager implements } } + @GuardedBy("mLock") boolean isPositionInSourceBounds(int displayId, float x, float y) { - synchronized (mLock) { - WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); - if (magnifier == null) { - return false; - } - return magnifier.isPositionInSourceBounds(x, y); + WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); + if (magnifier == null) { + return false; } + return magnifier.isPositionInSourceBounds(x, y); } /** @@ -962,7 +964,6 @@ public class WindowMagnificationManager implements return mIdOfLastServiceToControl; } - @GuardedBy("mLock") int pointersInWindow(MotionEvent motionEvent) { int count = 0; final int pointerCount = motionEvent.getPointerCount(); @@ -1050,8 +1051,16 @@ public class WindowMagnificationManager implements displayId, animationCallback); } + @GuardedBy("mLock") private boolean moveWindowMagnifierInternal(int displayId, float offsetX, float offsetY) { return mConnectionWrapper != null && mConnectionWrapper.moveWindowMagnifier( displayId, offsetX, offsetY); } + + @GuardedBy("mLock") + private boolean moveWindowMagnifierToPositionInternal(int displayId, float positionX, + float positionY, MagnificationAnimationCallback animationCallback) { + return mConnectionWrapper != null && mConnectionWrapper.moveWindowMagnifierToPosition( + displayId, positionX, positionY, animationCallback); + } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java index 3822dc362b6b8..4b77764c41e5e 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java @@ -93,6 +93,14 @@ public class WindowMagnificationConnectionWrapperTest { verify(mConnection).moveWindowMagnifier(TEST_DISPLAY, 100, 150); } + @Test + public void moveWindowMagnifierToPosition() throws RemoteException { + mConnectionWrapper.moveWindowMagnifierToPosition(TEST_DISPLAY, 100, 150, + mAnimationCallback); + verify(mConnection).moveWindowMagnifierToPosition(eq(TEST_DISPLAY), + eq(100f), eq(150f), any(IRemoteMagnificationAnimationCallback.class)); + } + @Test public void showMagnificationButton() throws RemoteException { mConnectionWrapper.showMagnificationButton(TEST_DISPLAY, 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 0742c09492f2a..f527d21786505 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 @@ -21,6 +21,8 @@ import static com.android.server.accessibility.magnification.MockWindowMagnifica import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.doAnswer; @@ -288,9 +290,8 @@ public class WindowMagnificationManagerTest { mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); - verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY), - eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()), - eq(0f), eq(0f), notNull()); + verify(mMockConnection.getConnection(), never()) + .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any()); } @@ -310,9 +311,8 @@ public class WindowMagnificationManagerTest { mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); - verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY), - eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()), - eq(0f), eq(0f), notNull()); + verify(mMockConnection.getConnection(), never()) + .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any()); } @Test @@ -328,8 +328,8 @@ public class WindowMagnificationManagerTest { mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); - verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY), - eq(3f), eq(500f), eq(500f), eq(0f), eq(0f), notNull()); + verify(mMockConnection.getConnection(), never()) + .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any()); } @Test @@ -345,9 +345,9 @@ public class WindowMagnificationManagerTest { mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); - verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f), + verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()), - eq(0f), eq(0f), notNull()); + any(IRemoteMagnificationAnimationCallback.class)); } @Test @@ -367,14 +367,13 @@ public class WindowMagnificationManagerTest { mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); - verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), - eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()), - eq(0f), eq(0f), notNull()); + verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY), + eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()), + any(IRemoteMagnificationAnimationCallback.class)); } @Test - public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnification() - throws RemoteException { + public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnification() { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); final Region beforeRegion = new Region(); From 74db862dd298dd82814465d64d697ae65cd70789 Mon Sep 17 00:00:00 2001 From: Daniel Hsieh Date: Tue, 15 Feb 2022 15:14:02 +0000 Subject: [PATCH 2/2] Enables following typing functionality when the window magnifier is on again. There are 3 conditions that we have to reset the tracking typing focus state to on. 1. IME is shown again. (It already exists.) 2. A user re-enable a window magnifier. 3. A user switch to a window magnifier from a full-screen magnifier. In this CL, we add the 2nd and 3rd condition to turn on this functionality in a window magnifier. Bug: 216394179 Test: atest WindowMagnificationManagerTest Change-Id: I78ca62b00559c242d5562f4247e3854e8386a512 --- .../WindowMagnificationManager.java | 10 ++-- .../WindowMagnificationManagerTest.java | 56 ++++++++++++++++--- 2 files changed, 53 insertions(+), 13 deletions(-) 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 f38dcf18db5cc..46998123cd873 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -360,7 +360,7 @@ public class WindowMagnificationManager implements * @param displayId The logical display id. * @param trackingTypingFocusEnabled Enabled or disable the function of tracking typing focus. */ - private void setTrackingTypingFocusEnabled(int displayId, boolean trackingTypingFocusEnabled) { + void setTrackingTypingFocusEnabled(int displayId, boolean trackingTypingFocusEnabled) { synchronized (mLock) { WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); if (magnifier == null) { @@ -503,8 +503,11 @@ public class WindowMagnificationManager implements animationCallback, windowPosition, id); } - if (enabled && !previousEnabled) { - mCallback.onWindowMagnificationActivationState(displayId, true); + if (enabled) { + setTrackingTypingFocusEnabled(displayId, true); + if (!previousEnabled) { + mCallback.onWindowMagnificationActivationState(displayId, true); + } } return enabled; } @@ -886,7 +889,6 @@ public class WindowMagnificationManager implements mWindowMagnificationManager = windowMagnificationManager; } - @GuardedBy("mLock") boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback, @WindowPosition int windowPosition, int id) { 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 f527d21786505..88ca7f539cb71 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 @@ -277,7 +277,7 @@ public class WindowMagnificationManagerTest { } @Test - public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnification() + public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnifier() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); @@ -296,7 +296,7 @@ public class WindowMagnificationManagerTest { @Test - public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnification() + public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnifier() throws RemoteException { final float distanceX = 10f; final float distanceY = 10f; @@ -316,7 +316,7 @@ public class WindowMagnificationManagerTest { } @Test - public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnification() + public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnifier() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); @@ -333,7 +333,7 @@ public class WindowMagnificationManagerTest { } @Test - public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnification() + public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnifier() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); @@ -351,12 +351,10 @@ public class WindowMagnificationManagerTest { } @Test - public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnification() + public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnifier() throws RemoteException { - final PointF initialPoint = new PointF(50f, 50f); mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, - initialPoint.x, initialPoint.y); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); mMockConnection.getConnectionCallback().onDrag(TEST_DISPLAY); mWindowMagnificationManager.onImeWindowVisibilityChanged(true); final Region outRegion = new Region(); @@ -373,7 +371,7 @@ public class WindowMagnificationManagerTest { } @Test - public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnification() { + public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnifier() { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); final Region beforeRegion = new Region(); @@ -390,6 +388,46 @@ public class WindowMagnificationManagerTest { assertEquals(afterRegion, beforeRegion); } + @Test + public void onRectangleOnScreenRequested_trackingDisabled_withoutMovingMagnifier() { + mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); + mWindowMagnificationManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false); + final Region beforeRegion = new Region(); + mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion); + final Rect requestedRect = beforeRegion.getBounds(); + requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10); + + mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, + requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); + + final Region afterRegion = new Region(); + mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion); + assertEquals(afterRegion, beforeRegion); + } + + @Test + public void onRectangleOnScreenRequested_trackingDisabledAndEnabledMagnifier_movingMagnifier() + throws RemoteException { + mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f); + mWindowMagnificationManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false); + final Region beforeRegion = new Region(); + mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion); + final Rect requestedRect = beforeRegion.getBounds(); + requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10); + mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false); + // Enabling a window magnifier again will turn on the tracking typing focus functionality. + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN); + + mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY, + requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom); + + verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY), + eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()), + any(IRemoteMagnificationAnimationCallback.class)); + } + @Test public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection());