diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl index ddf68fcb1311c..67d96678f420d 100644 --- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl +++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl @@ -38,9 +38,14 @@ oneway interface IWindowMagnificationConnection { * or {@link Float#NaN} to leave unchanged. * @param centerY the screen-relative Y coordinate around which to center, * or {@link Float#NaN} to leave unchanged. + * @param magnificationFrameOffsetRatioX Indicate the X coordinate offset between + * frame position X and centerX + * @param magnificationFrameOffsetRatioY Indicate the Y coordinate offset between + * frame position Y and centerY * @param callback The callback called when the animation is completed or interrupted. */ void enableWindowMagnification(int displayId, float scale, float centerX, float centerY, + float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, in IRemoteMagnificationAnimationCallback callback); /** diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java index 794b9dd5b68b6..a10efa982701d 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java @@ -158,12 +158,13 @@ public class WindowMagnification extends CoreStartable implements WindowMagnifie @MainThread void enableWindowMagnification(int displayId, float scale, float centerX, float centerY, + float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, @Nullable IRemoteMagnificationAnimationCallback callback) { final WindowMagnificationController windowMagnificationController = mMagnificationControllerSupplier.get(displayId); if (windowMagnificationController != null) { - windowMagnificationController.enableWindowMagnification(scale, centerX, - centerY, callback); + windowMagnificationController.enableWindowMagnification(scale, centerX, centerY, + magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, callback); } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java index 1bfa9c1a2a513..dc1e0054ff24b 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java @@ -62,6 +62,8 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp private final ValueAnimator mValueAnimator; private final AnimationSpec mStartSpec = new AnimationSpec(); private final AnimationSpec mEndSpec = new AnimationSpec(); + private float mMagnificationFrameOffsetRatioX = 0f; + private float mMagnificationFrameOffsetRatioY = 0f; private final Context mContext; // Called when the animation is ended successfully without cancelling or mStartSpec and // mEndSpec are equal. @@ -88,7 +90,8 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp } /** - * Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float)} + * Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float, + * float, float, IRemoteMagnificationAnimationCallback)} * with transition animation. If the window magnification is not enabled, the scale will start * from 1.0 and the center won't be changed during the animation. If {@link #mState} is * {@code STATE_DISABLING}, the animation runs in reverse. @@ -106,16 +109,48 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp */ void enableWindowMagnification(float scale, float centerX, float centerY, @Nullable IRemoteMagnificationAnimationCallback animationCallback) { + enableWindowMagnification(scale, centerX, centerY, 0f, 0f, animationCallback); + } + + /** + * Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float, + * float, float, IRemoteMagnificationAnimationCallback)} + * with transition animation. If the window magnification is not enabled, the scale will start + * from 1.0 and the center won't be changed during the animation. If {@link #mState} is + * {@code STATE_DISABLING}, the animation runs in reverse. + * + * @param scale The target scale, or {@link Float#NaN} to leave unchanged. + * @param centerX The screen-relative X coordinate around which to center for magnification, + * or {@link Float#NaN} to leave unchanged. + * @param centerY The screen-relative Y coordinate around which to center for magnification, + * or {@link Float#NaN} to leave unchanged. + * @param magnificationFrameOffsetRatioX Indicate the X coordinate offset between + * frame position X and centerX + * @param magnificationFrameOffsetRatioY Indicate the Y coordinate offset between + * frame position Y and centerY + * @param animationCallback Called when the transition is complete, the given arguments + * are as same as current values, or the transition is interrupted + * due to the new transition request. + * + * @see #onAnimationUpdate(ValueAnimator) + */ + void enableWindowMagnification(float scale, float centerX, float centerY, + float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, + @Nullable IRemoteMagnificationAnimationCallback animationCallback) { if (mController == null) { return; } sendAnimationCallback(false); + mMagnificationFrameOffsetRatioX = magnificationFrameOffsetRatioX; + mMagnificationFrameOffsetRatioY = magnificationFrameOffsetRatioY; + // Enable window magnification without animation immediately. if (animationCallback == null) { if (mState == STATE_ENABLING || mState == STATE_DISABLING) { mValueAnimator.cancel(); } - mController.enableWindowMagnification(scale, centerX, centerY); + mController.enableWindowMagnificationInternal(scale, centerX, centerY, + mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY); setState(STATE_ENABLED); return; } @@ -123,7 +158,8 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp setupEnableAnimationSpecs(scale, centerX, centerY); if (mEndSpec.equals(mStartSpec)) { if (mState == STATE_DISABLED) { - mController.enableWindowMagnification(scale, centerX, centerY); + mController.enableWindowMagnificationInternal(scale, centerX, centerY, + mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY); } else if (mState == STATE_ENABLING || mState == STATE_DISABLING) { mValueAnimator.cancel(); } @@ -273,7 +309,8 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp mStartSpec.mCenterX + (mEndSpec.mCenterX - mStartSpec.mCenterX) * fract; final float centerY = mStartSpec.mCenterY + (mEndSpec.mCenterY - mStartSpec.mCenterY) * fract; - mController.enableWindowMagnification(sentScale, centerX, centerY); + mController.enableWindowMagnificationInternal(sentScale, centerX, centerY, + mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY); } private static ValueAnimator newValueAnimator(Resources resources) { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java index 92cd8b183b62b..2133da202ce92 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java @@ -49,11 +49,13 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S } @Override - public void enableWindowMagnification(int displayId, float scale, float centerX, - float centerY, IRemoteMagnificationAnimationCallback callback) { + public void enableWindowMagnification(int displayId, float scale, float centerX, float centerY, + float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, + IRemoteMagnificationAnimationCallback callback) { mHandler.post( () -> mWindowMagnification.enableWindowMagnification(displayId, scale, centerX, - centerY, callback)); + centerY, magnificationFrameOffsetRatioX, + magnificationFrameOffsetRatioY, callback)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 250700487b7f5..b064ba904120f 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -95,17 +95,46 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold @Surface.Rotation @VisibleForTesting int mRotation; - private final Rect mMagnificationFrame = new Rect(); private final SurfaceControl.Transaction mTransaction; private final WindowManager mWm; private float mScale; + /** + * MagnificationFrame represents the bound of {@link #mMirrorSurface} and is constrained + * by the {@link #mMagnificationFrameBoundary}. + * We use MagnificationFrame to calculate the position of {@link #mMirrorView}. + * We combine MagnificationFrame with {@link #mMagnificationFrameOffsetX} and + * {@link #mMagnificationFrameOffsetY} to calculate the position of {@link #mSourceBounds}. + */ + private final Rect mMagnificationFrame = new Rect(); private final Rect mTmpRect = new Rect(); + + /** + * MirrorViewBounds is the bound of the {@link #mMirrorView} which displays the magnified + * content. + * {@link #mMirrorView}'s center is equal to {@link #mMagnificationFrame}'s center. + */ private final Rect mMirrorViewBounds = new Rect(); + + /** + * SourceBound is the bound of the magnified region which projects the magnified content. + * SourceBound's center is equal to the parameters centerX and centerY in + * {@link WindowMagnificationController#enableWindowMagnificationInternal(float, float, float)}} + * but it is calculated from {@link #mMagnificationFrame}'s center in the runtime. + */ private final Rect mSourceBounds = new Rect(); + /** + * The relation of centers between {@link #mSourceBounds} and {@link #mMagnificationFrame} is + * calculated in {@link #calculateSourceBounds(Rect, float)} and the equations are as following: + * MagnificationFrame = SourceBound (e.g., centerX & centerY) + MagnificationFrameOffset + * SourceBound = MagnificationFrame - MagnificationFrameOffset + */ + private int mMagnificationFrameOffsetX = 0; + private int mMagnificationFrameOffsetY = 0; + // The root of the mirrored content private SurfaceControl mMirrorSurface; @@ -123,6 +152,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private final Runnable mMirrorViewRunnable; private final Runnable mUpdateStateDescriptionRunnable; private final Runnable mWindowInsetChangeRunnable; + // MirrorView is the mirror window which displays the magnified content. private View mMirrorView; private SurfaceView mMirrorSurfaceView; private int mMirrorSurfaceMargin; @@ -339,7 +369,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold // window size changed not caused by rotation. if (isWindowVisible() && reCreateWindow) { deleteWindowMagnification(); - enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN); + enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); } } @@ -633,6 +663,26 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold int top = displayFrame.top + (halfHeight - (int) (halfHeight / scale)); int bottom = displayFrame.bottom - (halfHeight - (int) (halfHeight / scale)); mSourceBounds.set(left, top, right, bottom); + + // SourceBound's center is equal to center[X,Y] but calculated from MagnificationFrame's + // center. The relation between SourceBound and MagnificationFrame is as following: + // MagnificationFrame = SourceBound (center[X,Y]) + MagnificationFrameOffset + // SourceBound = MagnificationFrame - MagnificationFrameOffset + mSourceBounds.offset(-mMagnificationFrameOffsetX, -mMagnificationFrameOffsetY); + + if (mSourceBounds.left < 0) { + mSourceBounds.offsetTo(0, mSourceBounds.top); + } else if (mSourceBounds.right > mWindowBounds.width()) { + mSourceBounds.offsetTo(mWindowBounds.width() - mSourceBounds.width(), + mSourceBounds.top); + } + + if (mSourceBounds.top < 0) { + mSourceBounds.offsetTo(mSourceBounds.left, 0); + } else if (mSourceBounds.bottom > mWindowBounds.height()) { + mSourceBounds.offsetTo(mSourceBounds.left, + mWindowBounds.height() - mSourceBounds.height()); + } } private void calculateMagnificationFrameBoundary() { @@ -646,11 +696,31 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold final int scaledWidth = (int) (halfWidth / mScale); // The scaled half height of magnified region. final int scaledHeight = (int) (halfHeight / mScale); - final int exceededWidth = halfWidth - scaledWidth; - final int exceededHeight = halfHeight - scaledHeight; - mMagnificationFrameBoundary.set(-exceededWidth, -exceededHeight, - mWindowBounds.width() + exceededWidth, mWindowBounds.height() + exceededHeight); + // MagnificationFrameBoundary constrain the space of MagnificationFrame, and it also has + // to leave enough space for SourceBound to magnify the whole screen space. + // However, there is an offset between SourceBound and MagnificationFrame. + // The relation between SourceBound and MagnificationFrame is as following: + // SourceBound = MagnificationFrame - MagnificationFrameOffset + // Therefore, we have to adjust the exceededBoundary based on the offset. + // + // We have to increase the offset space for the SourceBound edges which are located in + // the MagnificationFrame. For example, if the offsetX and offsetY are negative, which + // means SourceBound is at right-bottom size of MagnificationFrame, the left and top + // edges of SourceBound are located in MagnificationFrame. So, we have to leave extra + // offset space at left and top sides and don't have to leave extra space at right and + // bottom sides. + final int exceededLeft = Math.max(halfWidth - scaledWidth - mMagnificationFrameOffsetX, 0); + final int exceededRight = Math.max(halfWidth - scaledWidth + mMagnificationFrameOffsetX, 0); + final int exceededTop = Math.max(halfHeight - scaledHeight - mMagnificationFrameOffsetY, 0); + final int exceededBottom = Math.max(halfHeight - scaledHeight + mMagnificationFrameOffsetY, + 0); + + mMagnificationFrameBoundary.set( + -exceededLeft, + -exceededTop, + mWindowBounds.width() + exceededRight, + mWindowBounds.height() + exceededBottom); } /** @@ -711,24 +781,30 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } /** - * Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float)} + * Wraps {@link WindowMagnificationController#enableWindowMagnificationInternal(float, float, + * float, float, float)} * with transition animation. If the window magnification is not enabled, the scale will start * from 1.0 and the center won't be changed during the animation. If animator is * {@code STATE_DISABLING}, the animation runs in reverse. * * @param scale The target scale, or {@link Float#NaN} to leave unchanged. - * @param centerX The screen-relative X coordinate around which to center, + * @param centerX The screen-relative X coordinate around which to center for magnification, * or {@link Float#NaN} to leave unchanged. - * @param centerY The screen-relative Y coordinate around which to center, + * @param centerY The screen-relative Y coordinate around which to center for magnification, * or {@link Float#NaN} to leave unchanged. + * @param magnificationFrameOffsetRatioX Indicate the X coordinate offset + * between frame position X and centerX + * @param magnificationFrameOffsetRatioY Indicate the Y coordinate offset + * between frame position Y and centerY * @param animationCallback Called when the transition is complete, the given arguments * 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, + float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, @Nullable IRemoteMagnificationAnimationCallback animationCallback) { - mAnimationController.enableWindowMagnification(scale, centerX, - centerY, animationCallback); + mAnimationController.enableWindowMagnification(scale, centerX, centerY, + magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, animationCallback); } /** @@ -738,21 +814,56 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold * be consistent with the behavior of display magnification. * * @param scale the target scale, or {@link Float#NaN} to leave unchanged - * @param centerX the screen-relative X coordinate around which to center, + * @param centerX the screen-relative X coordinate around which to center for magnification, * or {@link Float#NaN} to leave unchanged. - * @param centerY the screen-relative Y coordinate around which to center, + * @param centerY the screen-relative Y coordinate around which to center for magnification, * or {@link Float#NaN} to leave unchanged. */ - void enableWindowMagnification(float scale, float centerX, float centerY) { + void enableWindowMagnificationInternal(float scale, float centerX, float centerY) { + enableWindowMagnificationInternal(scale, centerX, centerY, Float.NaN, Float.NaN); + } + + /** + * Enables window magnification with specified parameters. If the given scale is less + * than or equal to 1.0f, then + * {@link WindowMagnificationController#deleteWindowMagnification()} will be called instead to + * be consistent with the behavior of display magnification. + * + * @param scale the target scale, or {@link Float#NaN} to leave unchanged + * @param centerX the screen-relative X coordinate around which to center for magnification, + * or {@link Float#NaN} to leave unchanged. + * @param centerY the screen-relative Y coordinate around which to center for magnification, + * or {@link Float#NaN} to leave unchanged. + * @param magnificationFrameOffsetRatioX Indicate the X coordinate offset + * between frame position X and centerX, + * or {@link Float#NaN} to leave unchanged. + * @param magnificationFrameOffsetRatioY Indicate the Y coordinate offset + * between frame position Y and centerY, + * or {@link Float#NaN} to leave unchanged. + */ + void enableWindowMagnificationInternal(float scale, float centerX, float centerY, + float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY) { if (Float.compare(scale, 1.0f) <= 0) { deleteWindowMagnification(); return; } + mMagnificationFrameOffsetX = Float.isNaN(magnificationFrameOffsetRatioX) + ? mMagnificationFrameOffsetX + : (int) (mMagnificationFrame.width() / 2 * magnificationFrameOffsetRatioX); + mMagnificationFrameOffsetY = Float.isNaN(magnificationFrameOffsetRatioY) + ? mMagnificationFrameOffsetY + : (int) (mMagnificationFrame.height() / 2 * magnificationFrameOffsetRatioY); + + // The relation of centers between SourceBound and MagnificationFrame is as following: + // MagnificationFrame = SourceBound (e.g., centerX & centerY) + MagnificationFrameOffset + final float newMagnificationFrameCenterX = centerX + mMagnificationFrameOffsetX; + final float newMagnificationFrameCenterY = centerY + mMagnificationFrameOffsetY; + final float offsetX = Float.isNaN(centerX) ? 0 - : centerX - mMagnificationFrame.exactCenterX(); + : newMagnificationFrameCenterX - mMagnificationFrame.exactCenterX(); final float offsetY = Float.isNaN(centerY) ? 0 - : centerY - mMagnificationFrame.exactCenterY(); + : newMagnificationFrameCenterY - mMagnificationFrame.exactCenterY(); mScale = Float.isNaN(scale) ? mScale : scale; calculateMagnificationFrameBoundary(); @@ -774,7 +885,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold if (mAnimationController.isAnimating() || !isWindowVisible() || mScale == scale) { return; } - enableWindowMagnification(scale, Float.NaN, Float.NaN); + enableWindowMagnificationInternal(scale, Float.NaN, Float.NaN); mHandler.removeCallbacks(mUpdateStateDescriptionRunnable); mHandler.postDelayed(mUpdateStateDescriptionRunnable, UPDATE_STATE_DESCRIPTION_DELAY_MS); } 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 326d902601378..796af115bf681 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java @@ -100,11 +100,11 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase { @Test public void enableWindowMagnification_passThrough() throws RemoteException { mIWindowMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN, - Float.NaN, mAnimationCallback); + Float.NaN, 0f, 0f, mAnimationCallback); waitForIdleSync(); verify(mWindowMagnificationController).enableWindowMagnification(eq(3.0f), - eq(Float.NaN), eq(Float.NaN), eq(mAnimationCallback)); + eq(Float.NaN), eq(Float.NaN), eq(0f), eq(0f), eq(mAnimationCallback)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java index 8bb9d423fa921..44770fab2d303 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java @@ -110,7 +110,7 @@ public class TestableWindowManager implements WindowManager { } /** - * Sets the given window insets to the current window metics. + * Sets the given window insets to the current window metrics. * * @param insets the window insets. */ 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 854fc33d768dc..3cc177dd8d91d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java @@ -17,22 +17,27 @@ package com.android.systemui.accessibility; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.animation.ValueAnimator; import android.annotation.Nullable; import android.app.Instrumentation; import android.content.Context; +import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; import android.os.SystemClock; import android.testing.AndroidTestingRunner; import android.view.SurfaceControl; +import android.view.View; +import android.view.WindowManager; import android.view.accessibility.IRemoteMagnificationAnimationCallback; import android.view.animation.AccelerateInterpolator; @@ -40,6 +45,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.model.SysUiState; @@ -56,7 +62,6 @@ import org.mockito.MockitoAnnotations; import java.util.concurrent.atomic.AtomicReference; - @Ignore @LargeTest @RunWith(AndroidTestingRunner.class) @@ -74,6 +79,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { private ArgumentCaptor mScaleCaptor = ArgumentCaptor.forClass(Float.class); private ArgumentCaptor mCenterXCaptor = ArgumentCaptor.forClass(Float.class); private ArgumentCaptor mCenterYCaptor = ArgumentCaptor.forClass(Float.class); + private final ArgumentCaptor mOffsetXCaptor = ArgumentCaptor.forClass(Float.class); + private final ArgumentCaptor mOffsetYCaptor = ArgumentCaptor.forClass(Float.class); @Mock Handler mHandler; @@ -94,10 +101,16 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { private long mWaitingAnimationPeriod; private long mWaitIntermediateAnimationPeriod; + private TestableWindowManager mWindowManager; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mInstrumentation = InstrumentationRegistry.getInstrumentation(); + final WindowManager wm = mContext.getSystemService(WindowManager.class); + mWindowManager = spy(new TestableWindowManager(wm)); + mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); + mWaitingAnimationPeriod = 2 * ANIMATION_DURATION_MS; mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2; mWindowMagnificationAnimationController = new WindowMagnificationAnimationController( @@ -119,12 +132,15 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { throws RemoteException { enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); - verify(mSpyController, atLeast(2)).enableWindowMagnification( + verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( mScaleCaptor.capture(), - mCenterXCaptor.capture(), mCenterYCaptor.capture()); + mCenterXCaptor.capture(), mCenterYCaptor.capture(), + mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); verifyStartValue(mScaleCaptor, 1.0f); verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X); verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y); + verifyStartValue(mOffsetXCaptor, 0f); + verifyStartValue(mOffsetYCaptor, 0f); verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); verify(mAnimationCallback).onResult(true); } @@ -162,8 +178,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { }); SystemClock.sleep(mWaitingAnimationPeriod); - verify(mSpyController).enableWindowMagnification(1, DEFAULT_CENTER_X, - DEFAULT_CENTER_Y); + verify(mSpyController).enableWindowMagnificationInternal(1, DEFAULT_CENTER_X, + DEFAULT_CENTER_Y, 0f, 0f); verify(mAnimationCallback).onResult(true); } @@ -187,11 +203,15 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { SystemClock.sleep(mWaitingAnimationPeriod); - verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), - mCenterXCaptor.capture(), mCenterYCaptor.capture()); + verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( + mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture(), + mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); verifyStartValue(mScaleCaptor, mCurrentScale.get()); verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); + verifyStartValue(mOffsetXCaptor, 0f); + verifyStartValue(mOffsetYCaptor, 0f); verifyFinalSpec(targetScale, targetCenterX, targetCenterY); verify(mAnimationCallback).onResult(false); verify(mAnimationCallback2).onResult(true); @@ -213,11 +233,15 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { SystemClock.sleep(mWaitingAnimationPeriod); - verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), - mCenterXCaptor.capture(), mCenterYCaptor.capture()); + verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( + mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture(), + mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); verifyStartValue(mScaleCaptor, mCurrentScale.get()); verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); + verifyStartValue(mOffsetXCaptor, 0f); + verifyStartValue(mOffsetYCaptor, 0f); // It presents the window magnification is disabled. verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); @@ -256,7 +280,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { }); SystemClock.sleep(mWaitingAnimationPeriod); - verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(), + verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(), anyFloat()); verify(mAnimationCallback).onResult(false); verify(mAnimationCallback2).onResult(true); @@ -286,9 +310,10 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { verify(mAnimationCallback).onResult(false); SystemClock.sleep(mWaitingAnimationPeriod); - verify(mSpyController, atLeast(2)).enableWindowMagnification( + verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( mScaleCaptor.capture(), - mCenterXCaptor.capture(), mCenterYCaptor.capture()); + mCenterXCaptor.capture(), mCenterYCaptor.capture(), + mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); //Animating in reverse, so we only check if the start values are greater than current. assertTrue(mScaleCaptor.getAllValues().get(0) > mCurrentScale.get()); assertEquals(targetScale, mScaleCaptor.getValue(), 0f); @@ -336,7 +361,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { }); SystemClock.sleep(mWaitingAnimationPeriod); - verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(), + verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(), anyFloat()); verify(mSpyController, never()).deleteWindowMagnification(); verify(mAnimationCallback).onResult(false); @@ -362,15 +387,43 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { SystemClock.sleep(mWaitingAnimationPeriod); - verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), - mCenterXCaptor.capture(), mCenterYCaptor.capture()); + verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( + mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture(), + mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); verifyStartValue(mScaleCaptor, mCurrentScale.get()); verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); + verifyStartValue(mOffsetXCaptor, 0f); + verifyStartValue(mOffsetYCaptor, 0f); verifyFinalSpec(targetScale, targetCenterX, targetCenterY); verify(mAnimationCallback2).onResult(true); } + @Test + public void enableWindowMagnificationWithOffset_expectedValues() { + final float offsetRatio = -0.1f; + final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds()); + mInstrumentation.runOnMainSync(() -> { + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, + windowBounds.exactCenterX(), windowBounds.exactCenterY(), + offsetRatio, offsetRatio, mAnimationCallback); + }); + SystemClock.sleep(mWaitingAnimationPeriod); + final View attachedView = mWindowManager.getAttachedView(); + assertNotNull(attachedView); + final Rect mirrorViewBound = new Rect(); + final View mirrorView = attachedView.findViewById(R.id.surface_view); + assertNotNull(mirrorView); + mirrorView.getBoundsOnScreen(mirrorViewBound); + + assertEquals(mirrorViewBound.exactCenterX() - windowBounds.exactCenterX(), + Math.round(offsetRatio * mirrorViewBound.width() / 2), 0.1f); + assertEquals(mirrorViewBound.exactCenterY() - windowBounds.exactCenterY(), + Math.round(offsetRatio * mirrorViewBound.height() / 2), 0.1f); + } + @Test public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback() throws RemoteException { @@ -378,7 +431,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); - verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(), + verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(), anyFloat()); verify(mAnimationCallback).onResult(true); } @@ -390,11 +443,15 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); - verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), - mCenterXCaptor.capture(), mCenterYCaptor.capture()); + verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( + mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture(), + mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); verifyStartValue(mScaleCaptor, DEFAULT_SCALE); verifyStartValue(mCenterXCaptor, Float.NaN); verifyStartValue(mCenterYCaptor, Float.NaN); + verifyStartValue(mOffsetXCaptor, 0f); + verifyStartValue(mOffsetYCaptor, 0f); verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); verify(mAnimationCallback).onResult(true); } @@ -433,8 +490,10 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { mCurrentCenterY.set(mController.getCenterY()); }); SystemClock.sleep(mWaitingAnimationPeriod); - verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), - mCenterXCaptor.capture(), mCenterYCaptor.capture()); + verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( + mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture(), + mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); //The animation is in verse, so we only check the start values should no be greater than // the current one. @@ -442,6 +501,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { assertEquals(1.0f, mScaleCaptor.getValue(), 0f); verifyStartValue(mCenterXCaptor, Float.NaN); verifyStartValue(mCenterYCaptor, Float.NaN); + verifyStartValue(mOffsetXCaptor, 0f); + verifyStartValue(mOffsetYCaptor, 0f); verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); verify(mAnimationCallback).onResult(false); verify(mAnimationCallback2).onResult(true); @@ -471,9 +532,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback2); - verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), - mCenterXCaptor.capture(), mCenterYCaptor.capture()); + verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( + mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture(), + mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); assertEquals(1.0f, mScaleCaptor.getValue(), 0f); + verifyStartValue(mOffsetXCaptor, 0f); + verifyStartValue(mOffsetYCaptor, 0f); verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); verify(mAnimationCallback).onResult(false); verify(mAnimationCallback2).onResult(true); @@ -571,9 +636,18 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { } @Override - void enableWindowMagnification(float scale, float centerX, float centerY) { - super.enableWindowMagnification(scale, centerX, centerY); - mSpyController.enableWindowMagnification(scale, centerX, centerY); + void enableWindowMagnificationInternal(float scale, float centerX, float centerY) { + super.enableWindowMagnificationInternal(scale, centerX, centerY); + mSpyController.enableWindowMagnificationInternal(scale, centerX, centerY); + } + + @Override + void enableWindowMagnificationInternal(float scale, float centerX, float centerY, + float magnificationOffsetFrameRatioX, float magnificationOffsetFrameRatioY) { + super.enableWindowMagnificationInternal(scale, centerX, centerY, + magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY); + mSpyController.enableWindowMagnificationInternal(scale, centerX, centerY, + magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY); } @Override 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 9a3046554e0ce..8fdcaddc93fbd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -146,7 +146,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void enableWindowMagnification_showControlAndNotifyBoundsChanged() { mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); }); @@ -159,7 +159,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void enableWindowMagnification_systemGestureExclusionRectsIsSet() { mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); }); // Wait for Rects updated. @@ -180,7 +180,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState); mInstrumentation.runOnMainSync(() -> { - controller.enableWindowMagnification(Float.NaN, Float.NaN, + controller.enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); }); @@ -195,7 +195,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void deleteWindowMagnification_destroyControl() { mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); }); @@ -213,7 +213,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { setSystemGestureInsets(); mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, bounds.bottom); }); ReferenceTestUtils.waitForCondition(this::hasMagnificationOverlapFlag); @@ -229,7 +229,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void moveMagnifier_schedulesFrame() { mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); mWindowMagnificationController.moveWindowMagnifier(100f, 100f); }); @@ -246,8 +246,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { }).when(mHandler).postDelayed(any(Runnable.class), anyLong()); mInstrumentation.runOnMainSync( - () -> mWindowMagnificationController.enableWindowMagnification(2.0f, Float.NaN, - Float.NaN)); + () -> mWindowMagnificationController.enableWindowMagnificationInternal(2.0f, + Float.NaN, Float.NaN)); mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.setScale(3.0f)); @@ -283,8 +283,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { final float displayWidth = windowBounds.width(); final PointF magnifiedCenter = new PointF(center, center + 5f); mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, magnifiedCenter.x, - magnifiedCenter.y); + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + magnifiedCenter.x, magnifiedCenter.y); // Get the center again in case the center we set is out of screen. magnifiedCenter.set(mWindowMagnificationController.getCenterX(), mWindowMagnificationController.getCenterY()); @@ -327,7 +327,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { testWindowBounds.set(testWindowBounds.left, testWindowBounds.top, testWindowBounds.right + 100, testWindowBounds.bottom + 100); mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); }); mWindowManager.setWindowBounds(testWindowBounds); @@ -347,7 +347,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void screenSizeIsChangedToLarge_enabled_windowSizeIsConstrained() { mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); }); final int screenSize = mContext.getResources().getDimensionPixelSize( @@ -369,7 +369,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() { mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); Mockito.reset(mWindowManager); Mockito.reset(mMirrorWindowControl); @@ -398,7 +398,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void initializeA11yNode_enabled_expectedValues() { mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(2.5f, Float.NaN, Float.NaN); }); final View mirrorView = mWindowManager.getAttachedView(); @@ -422,7 +422,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { public void performA11yActions_visible_expectedResults() { final int displayId = mContext.getDisplayId(); mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(2.5f, Float.NaN, Float.NaN); }); @@ -449,7 +449,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { public void performA11yActions_visible_notifyAccessibilityActionPerformed() { final int displayId = mContext.getDisplayId(); mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(2.5f, Float.NaN, Float.NaN); }); @@ -462,7 +462,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void enableWindowMagnification_hasA11yWindowTitle() { mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); }); @@ -473,12 +473,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void enableWindowMagnificationWithScaleLessThanOne_enabled_disabled() { mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); }); mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(0.9f, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(0.9f, Float.NaN, Float.NaN); }); @@ -489,7 +489,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { public void onLocaleChanged_enabled_updateA11yWindowTitle() { final String newA11yWindowTitle = "new a11y window title"; mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); }); final TestableResources testableResources = getContext().getOrCreateTestableResources(); @@ -506,7 +506,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void onSingleTap_enabled_scaleIsChanged() { mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); }); @@ -530,7 +530,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); setSystemGestureInsets(); mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, Float.NaN); }); 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 5277425364802..25dcc2aea41b8 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java @@ -59,15 +59,19 @@ class WindowMagnificationConnectionWrapper { } boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY, + float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, @Nullable MagnificationAnimationCallback callback) { if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) { mTrace.logTrace(TAG + ".enableWindowMagnification", FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX - + ";centerY=" + centerY + ";callback=" + callback); + + ";centerY=" + centerY + ";magnificationFrameOffsetRatioX=" + + magnificationFrameOffsetRatioX + ";magnificationFrameOffsetRatioY=" + + magnificationFrameOffsetRatioY + ";callback=" + callback); } try { mConnection.enableWindowMagnification(displayId, scale, centerX, centerY, + magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, transformToRemoteCallback(callback, mTrace)); } catch (RemoteException e) { if (DBG) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java index 7d8f545b65c3c..820be28387aed 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java @@ -18,6 +18,7 @@ package com.android.server.accessibility.magnification; import static android.view.InputDevice.SOURCE_TOUCHSCREEN; import static android.view.MotionEvent.ACTION_CANCEL; +import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_UP; import static java.util.Arrays.asList; @@ -78,6 +79,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl final DetectingState mDetectingState; @VisibleForTesting final PanningScalingGestureState mObservePanningScalingState; + @VisibleForTesting + final ViewportDraggingState mViewportDraggingState; @VisibleForTesting State mCurrentState; @@ -105,6 +108,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl policyFlags)); mDelegatingState = new DelegatingState(mMotionEventDispatcherDelegate); mDetectingState = new DetectingState(context, mDetectTripleTap); + mViewportDraggingState = new ViewportDraggingState(); mObservePanningScalingState = new PanningScalingGestureState( new PanningScalingHandler(context, MAX_SCALE, MIN_SCALE, true, new PanningScalingHandler.MagnificationDelegate() { @@ -158,7 +162,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl public void handleShortcutTriggered() { final Point screenSize = mTempPoint; getScreenSize(mTempPoint); - toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f); + toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f, + WindowMagnificationManager.WINDOW_POSITION_AT_CENTER); } private void getScreenSize(Point outSize) { @@ -171,14 +176,17 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl return Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; } - private void enableWindowMagnifier(float centerX, float centerY) { + private void enableWindowMagnifier(float centerX, float centerY, + @WindowMagnificationManager.WindowPosition int windowPosition) { if (DEBUG_ALL) { - Slog.i(mLogTag, "enableWindowMagnifier :" + centerX + ", " + centerY); + Slog.i(mLogTag, "enableWindowMagnifier :" + + centerX + ", " + centerY + ", " + windowPosition); } final float scale = MathUtils.constrain( mWindowMagnificationMgr.getPersistedScale(mDisplayId), MIN_SCALE, MAX_SCALE); - mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY); + mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY, + windowPosition); } private void disableWindowMagnifier() { @@ -188,11 +196,12 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, false); } - private void toggleMagnification(float centerX, float centerY) { + private void toggleMagnification(float centerX, float centerY, + @WindowMagnificationManager.WindowPosition int windowPosition) { if (mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)) { disableWindowMagnifier(); } else { - enableWindowMagnifier(centerX, centerY); + enableWindowMagnifier(centerX, centerY, windowPosition); } } @@ -200,7 +209,17 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl if (DEBUG_DETECTING) { Slog.i(mLogTag, "onTripleTap()"); } - toggleMagnification(up.getX(), up.getY()); + toggleMagnification(up.getX(), up.getY(), + WindowMagnificationManager.WINDOW_POSITION_AT_CENTER); + } + + private void onTripleTapAndHold(MotionEvent up) { + if (DEBUG_DETECTING) { + Slog.i(mLogTag, "onTripleTapAndHold()"); + } + enableWindowMagnifier(up.getX(), up.getY(), + WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT); + transitionTo(mViewportDraggingState); } void resetToDetectState() { @@ -319,6 +338,65 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl } } + + /** + * This class handles motion events when the event dispatcher has + * determined that the user is performing a single-finger drag of the + * magnification viewport. + * + * Leaving this state until receiving {@link MotionEvent#ACTION_UP} + * or {@link MotionEvent#ACTION_CANCEL}. + */ + final class ViewportDraggingState implements State { + + private float mLastX = Float.NaN; + private float mLastY = Float.NaN; + + @Override + public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + final int action = event.getActionMasked(); + switch (action) { + case ACTION_MOVE: { + if (!Float.isNaN(mLastX) && !Float.isNaN(mLastY)) { + float offsetX = event.getX() - mLastX; + float offsetY = event.getY() - mLastY; + mWindowMagnificationMgr.moveWindowMagnification(mDisplayId, offsetX, + offsetY); + } + mLastX = event.getX(); + mLastY = event.getY(); + } + break; + + case ACTION_UP: + case ACTION_CANCEL: { + mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true); + transitionTo(mDetectingState); + } + break; + } + } + + @Override + public void clear() { + mLastX = Float.NaN; + mLastY = Float.NaN; + } + + @Override + public void onExit() { + clear(); + } + + @Override + public String toString() { + return "ViewportDraggingState{" + + "mLastX=" + mLastX + + ",mLastY=" + mLastY + + '}'; + } + } + /** * This class handles motion events in a duration to determine if the user is going to * manipulate the window magnifier or want to interact with current UI. The rule of leaving @@ -405,6 +483,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl transitionTo(mObservePanningScalingState); } else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP) { onTripleTap(motionEvent); + } else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD) { + onTripleTapAndHold(motionEvent); } else { mMotionEventDispatcherDelegate.sendDelayedMotionEvents(delayedEventQueue, lastDownEventTime); @@ -439,6 +519,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl return "WindowMagnificationGestureHandler{" + "mDetectingState=" + mDetectingState + ", mDelegatingState=" + mDelegatingState + + ", mViewportDraggingState=" + mViewportDraggingState + ", mMagnifiedInteractionState=" + mObservePanningScalingState + ", mCurrentState=" + State.nameOf(mCurrentState) + ", mPreviousState=" + State.nameOf(mPreviousState) 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 d34b4a9f3ca76..9162064780caa 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -20,12 +20,14 @@ import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNI import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK; import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; import android.os.Binder; @@ -44,6 +46,9 @@ import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.statusbar.StatusBarManagerInternal; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * A class to manipulate window magnification through {@link WindowMagnificationConnectionWrapper} * create by {@link #setConnection(IWindowMagnificationConnection)}. To set the connection with @@ -58,6 +63,25 @@ public class WindowMagnificationManager implements private static final String TAG = "WindowMagnificationMgr"; + /** + * Indicate that the magnification window is at the magnification center. + */ + public static final int WINDOW_POSITION_AT_CENTER = 0; + + /** + * Indicate that the magnification window is at the top-left side of the magnification + * center. The offset is equal to a half of MirrorSurfaceView. So, the bottom-right corner + * of the window is at the magnification center. + */ + public static final int WINDOW_POSITION_AT_TOP_LEFT = 1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "WINDOW_POSITION_AT_" }, value = { + WINDOW_POSITION_AT_CENTER, + WINDOW_POSITION_AT_TOP_LEFT + }) + public @interface WindowPosition {} + private final Object mLock = new Object(); private final Context mContext; @VisibleForTesting @@ -281,20 +305,60 @@ public class WindowMagnificationManager implements } /** - * Enables window magnification with specified center and scale on the specified display and + * Enables window magnification with specified center and scale on the given display and * animating the transition. * * @param displayId The logical display id. * @param scale The target scale, must be >= 1. - * @param centerX The screen-relative X coordinate around which to center, + * @param centerX The screen-relative X coordinate around which to center for magnification, * or {@link Float#NaN} to leave unchanged. - * @param centerY The screen-relative Y coordinate around which to center, + * @param centerY The screen-relative Y coordinate around which to center for magnification, * or {@link Float#NaN} to leave unchanged. * @param animationCallback Called when the animation result is valid. * @return {@code true} if the magnification is enabled successfully. */ public boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback) { + return enableWindowMagnification(displayId, scale, centerX, centerY, animationCallback, + WINDOW_POSITION_AT_CENTER); + } + + /** + * Enables window magnification with specified center and scale on the given display and + * animating the transition. + * + * @param displayId The logical display id. + * @param scale The target scale, must be >= 1. + * @param centerX The screen-relative X coordinate around which to center for magnification, + * or {@link Float#NaN} to leave unchanged. + * @param centerY The screen-relative Y coordinate around which to center for magnification, + * or {@link Float#NaN} to leave unchanged. + * @param windowPosition Indicate the offset between window position and (centerX, centerY). + * @return {@code true} if the magnification is enabled successfully. + */ + public boolean enableWindowMagnification(int displayId, float scale, float centerX, + float centerY, @WindowPosition int windowPosition) { + return enableWindowMagnification(displayId, scale, centerX, centerY, + STUB_ANIMATION_CALLBACK, windowPosition); + } + + /** + * Enables window magnification with specified center and scale on the given display and + * animating the transition. + * + * @param displayId The logical display id. + * @param scale The target scale, must be >= 1. + * @param centerX The screen-relative X coordinate around which to center for + * magnification, or {@link Float#NaN} to leave unchanged. + * @param centerY The screen-relative Y coordinate around which to center for + * magnification, or {@link Float#NaN} to leave unchanged. + * @param animationCallback Called when the animation result is valid. + * @param windowPosition Indicate the offset between window position and (centerX, centerY). + * @return {@code true} if the magnification is enabled successfully. + */ + public boolean enableWindowMagnification(int displayId, float scale, float centerX, + float centerY, @Nullable MagnificationAnimationCallback animationCallback, + @WindowPosition int windowPosition) { final boolean enabled; boolean previousEnabled; synchronized (mLock) { @@ -307,7 +371,7 @@ public class WindowMagnificationManager implements } previousEnabled = magnifier.mEnabled; enabled = magnifier.enableWindowMagnificationInternal(scale, centerX, centerY, - animationCallback); + animationCallback, windowPosition); } if (enabled && !previousEnabled) { @@ -662,6 +726,8 @@ public class WindowMagnificationManager implements // The magnified bounds on the screen. private final Rect mSourceBounds = new Rect(); + private PointF mMagnificationFrameOffsetRatio = new PointF(0f, 0f); + WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) { mDisplayId = displayId; mWindowMagnificationManager = windowMagnificationManager; @@ -669,14 +735,17 @@ public class WindowMagnificationManager implements @GuardedBy("mLock") boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY, - @Nullable MagnificationAnimationCallback animationCallback) { + @Nullable MagnificationAnimationCallback animationCallback, + @WindowPosition int windowPosition) { // Handle defaults. The scale may be NAN when just updating magnification center. if (Float.isNaN(scale)) { scale = getScale(); } final float normScale = MagnificationScaleProvider.constrainScale(scale); + setMagnificationFrameOffsetRatioByWindowPosition(windowPosition); if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale, - centerX, centerY, animationCallback)) { + centerX, centerY, mMagnificationFrameOffsetRatio.x, + mMagnificationFrameOffsetRatio.y, animationCallback)) { mScale = normScale; mEnabled = true; @@ -685,6 +754,19 @@ public class WindowMagnificationManager implements return false; } + void setMagnificationFrameOffsetRatioByWindowPosition(@WindowPosition int windowPosition) { + switch (windowPosition) { + case WINDOW_POSITION_AT_CENTER: { + mMagnificationFrameOffsetRatio.set(0f, 0f); + } + break; + case WINDOW_POSITION_AT_TOP_LEFT: { + mMagnificationFrameOffsetRatio.set(-1f, -1f); + } + break; + } + } + @GuardedBy("mLock") boolean disableWindowMagnificationInternal( @Nullable MagnificationAnimationCallback animationResultCallback) { @@ -768,9 +850,15 @@ public class WindowMagnificationManager implements } private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX, - float centerY, MagnificationAnimationCallback animationCallback) { - return mConnectionWrapper != null && mConnectionWrapper.enableWindowMagnification( - displayId, scale, centerX, centerY, animationCallback); + float centerY, float magnificationFrameOffsetRatioX, + float magnificationFrameOffsetRatioY, + MagnificationAnimationCallback animationCallback) { + synchronized (mLock) { + return mConnectionWrapper != null && mConnectionWrapper.enableWindowMagnification( + displayId, scale, centerX, centerY, + magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, + animationCallback); + } } private boolean setScaleInternal(int displayId, float scale) { 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 0863f9e6a95d0..ec1a0c2f1d0d7 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 @@ -155,7 +155,7 @@ public class MagnificationControllerTest { verify(mScreenMagnificationController, never()).reset(anyInt(), any(MagnificationAnimationCallback.class)); verify(mMockConnection.getConnection(), never()).enableWindowMagnification(anyInt(), - anyFloat(), anyFloat(), anyFloat(), + anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), nullable(IRemoteMagnificationAnimationCallback.class)); } 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 2a5350454f674..0659a60193365 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 @@ -93,7 +93,7 @@ class MockWindowMagnificationConnection { final float scale = invocation.getArgument(1); mScale = Float.isNaN(scale) ? mScale : scale; computeMirrorWindowFrame(invocation.getArgument(2), invocation.getArgument(3)); - setAnimationCallback(invocation.getArgument(4)); + setAnimationCallback(invocation.getArgument(6)); computeSourceBounds(); mHasPendingCallback = true; if (!mSuspendCallback) { @@ -101,7 +101,7 @@ class MockWindowMagnificationConnection { } return null; }).when(mConnection).enableWindowMagnification(anyInt(), anyFloat(), anyFloat(), anyFloat(), - nullable(IRemoteMagnificationAnimationCallback.class)); + anyFloat(), anyFloat(), nullable(IRemoteMagnificationAnimationCallback.class)); } private void stubDisableWindowMagnification() throws RemoteException { 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 1638563e82422..3822dc362b6b8 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 @@ -67,7 +67,7 @@ public class WindowMagnificationConnectionWrapperTest { @Test public void enableWindowMagnification() throws RemoteException { mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f, - mAnimationCallback); + 0f, 0f, mAnimationCallback); verify(mAnimationCallback).onResult(true); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java index 1b8aff50d2e2a..b807c11d5a5c4 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.mock; import android.graphics.PointF; import android.graphics.Rect; import android.os.RemoteException; +import android.os.SystemClock; import android.testing.TestableContext; import android.util.DebugUtils; import android.view.InputDevice; @@ -58,10 +59,11 @@ public class WindowMagnificationGestureHandlerTest { public static final int STATE_SHOW_MAGNIFIER_SHORTCUT = 2; public static final int STATE_TWO_FINGERS_DOWN = 3; public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP = 4; + public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD = 5; //TODO: Test it after can injecting Handler to GestureMatcher is available. public static final int FIRST_STATE = STATE_IDLE; - public static final int LAST_STATE = STATE_SHOW_MAGNIFIER_TRIPLE_TAP; + public static final int LAST_STATE = STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD; // Co-prime x and y, to potentially catch x-y-swapped errors public static final float DEFAULT_TAP_X = 301; @@ -178,6 +180,12 @@ public class WindowMagnificationGestureHandlerTest { == mWindowMagnificationGestureHandler.mDetectingState, state); } break; + case STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: { + check(isWindowMagnifierEnabled(DISPLAY_0), state); + check(mWindowMagnificationGestureHandler.mCurrentState + == mWindowMagnificationGestureHandler.mViewportDraggingState, state); + } + break; case STATE_TWO_FINGERS_DOWN: { check(isWindowMagnifierEnabled(DISPLAY_0), state); check(mWindowMagnificationGestureHandler.mCurrentState @@ -229,6 +237,13 @@ public class WindowMagnificationGestureHandlerTest { tap(); } break; + case STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: { + // Perform triple tap and hold gesture + tap(); + tap(); + tapAndHold(); + } + break; default: throw new IllegalArgumentException("Illegal state: " + state); } @@ -262,6 +277,10 @@ public class WindowMagnificationGestureHandlerTest { tap(); } break; + case STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: { + send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y)); + } + break; default: throw new IllegalArgumentException("Illegal state: " + state); } @@ -308,6 +327,11 @@ public class WindowMagnificationGestureHandlerTest { send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y)); } + private void tapAndHold() { + send(downEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y)); + SystemClock.sleep(ViewConfiguration.getLongPressTimeout() + 100); + } + private String stateDump() { return "\nCurrent state dump:\n" + mWindowMagnificationGestureHandler.mCurrentState; } 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 02c0aca5c7773..85512f36da41d 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 @@ -143,8 +143,7 @@ public class WindowMagnificationManagerTest { * new connection. */ @Test - public void - setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath() + public void setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); MockWindowMagnificationConnection secondConnection = @@ -177,7 +176,7 @@ public class WindowMagnificationManagerTest { mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f); verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f), - eq(200f), eq(300f), notNull()); + eq(200f), eq(300f), eq(0f), eq(0f), notNull()); } @Test @@ -189,7 +188,8 @@ public class WindowMagnificationManagerTest { mAnimationCallback); verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f), - eq(200f), eq(300f), any(IRemoteMagnificationAnimationCallback.class)); + eq(200f), eq(300f), eq(0f), eq(0f), + any(IRemoteMagnificationAnimationCallback.class)); verify(mAnimationCallback).onResult(true); } @@ -410,6 +410,34 @@ public class WindowMagnificationManagerTest { assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f); } + @Test + public void centerGetter_enabledOnTestDisplayWindowAtCenter_expectedValues() + throws RemoteException { + mWindowMagnificationManager.requestConnection(true); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, + 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER); + + assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f); + assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f); + + verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f), + eq(100f), eq(200f), eq(0f), eq(0f), notNull()); + } + + @Test + public void centerGetter_enabledOnTestDisplayWindowAtLeftTop_expectedValues() + throws RemoteException { + mWindowMagnificationManager.requestConnection(true); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, + 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT); + + assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f); + assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f); + + verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f), + eq(100f), eq(200f), eq(-1f), eq(-1f), notNull()); + } + @Test public void onDisplayRemoved_enabledOnTestDisplay_disabled() { mWindowMagnificationManager.requestConnection(true);