From f1dfa43869a21bc09dea4bfabdcafc1c6da922f3 Mon Sep 17 00:00:00 2001 From: ryanlwlin Date: Thu, 30 Dec 2021 13:47:46 +0800 Subject: [PATCH] Support set window size API It provides resize api which constraint the minimum and maximum window size. Since the magnification window could be an arbitary rectangle, We need to change the window size and the center based on the transformation result. Bug: 188136191 Test: atest WindowMagnificationControllerTest Change-Id: I63abe89d087988460efc7d0586c5a309e6142768 (cherry picked from commit 9555ceb3945f38e32473b29ab0cebd31f92ecf0b) Merged-In: I63abe89d087988460efc7d0586c5a309e6142768 --- core/res/res/values/dimens.xml | 3 + core/res/res/values/symbols.xml | 1 + .../WindowMagnificationController.java | 115 ++++++++++++---- .../WindowMagnificationControllerTest.java | 124 +++++++++++++++++- 4 files changed, 207 insertions(+), 36 deletions(-) diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 744c3dab9510a..adf8f8e99d352 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -606,6 +606,9 @@ 21.88% + + 122dp + 8dp diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e7eeecc2b516c..bc488c088a476 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4398,6 +4398,7 @@ + diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 0d20403b08f2e..de03993a6f17e 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -43,6 +43,7 @@ import android.os.Handler; import android.os.RemoteException; import android.util.Log; import android.util.Range; +import android.util.Size; import android.view.Choreographer; import android.view.Display; import android.view.Gravity; @@ -62,6 +63,8 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.IRemoteMagnificationAnimationCallback; +import androidx.core.math.MathUtils; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.R; @@ -166,6 +169,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private final Rect mMagnificationFrameBoundary = new Rect(); // The top Y of the system gesture rect at the bottom. Set to -1 if it is invalid. private int mSystemGestureTop = -1; + private int mMinWindowSize; private final WindowMagnificationAnimationController mAnimationController; private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; @@ -208,8 +212,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mBounceEffectDuration = mResources.getInteger( com.android.internal.R.integer.config_shortAnimTime); updateDimensions(); - setMagnificationFrameWith(mWindowBounds, mWindowBounds.width() / 2, - mWindowBounds.height() / 2); + + final Size windowSize = getDefaultWindowSizeWithWindowBounds(mWindowBounds); + setMagnificationFrame(windowSize.getWidth(), windowSize.getHeight(), + mWindowBounds.width() / 2, mWindowBounds.height() / 2); computeBounceAnimationScale(); mMirrorWindowControl = mirrorWindowControl; @@ -281,6 +287,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold R.dimen.magnification_drag_view_size); mOuterBorderSize = mResources.getDimensionPixelSize( R.dimen.magnification_outer_border_margin); + mMinWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); } private void computeBounceAnimationScale() { @@ -414,9 +422,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return false; } mWindowBounds.set(currentWindowBounds); + final Size windowSize = getDefaultWindowSizeWithWindowBounds(mWindowBounds); final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width(); final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height(); - setMagnificationFrameWith(mWindowBounds, (int) newCenterX, (int) newCenterY); + + setMagnificationFrame(windowSize.getWidth(), windowSize.getHeight(), (int) newCenterX, + (int) newCenterY); calculateMagnificationFrameBoundary(); return true; } @@ -454,11 +465,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mWindowBounds.set(currentWindowBounds); - calculateMagnificationFrameBoundary(); - - if (!isWindowVisible()) { - return; - } // Keep MirrorWindow position on the screen unchanged when device rotates 90° // clockwise or anti-clockwise. @@ -469,14 +475,13 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } else if (rotationDegree == 270) { matrix.postTranslate(0, mWindowBounds.height()); } - // The rect of MirrorView is going to be transformed. - LayoutParams params = - (LayoutParams) mMirrorView.getLayoutParams(); - mTmpRect.set(params.x, params.y, params.x + params.width, params.y + params.height); - final RectF transformedRect = new RectF(mTmpRect); + + final RectF transformedRect = new RectF(mMagnificationFrame); + // The window frame is going to be transformed by the rotation matrix. + transformedRect.inset(-mMirrorSurfaceMargin, -mMirrorSurfaceMargin); matrix.mapRect(transformedRect); - moveWindowMagnifier(transformedRect.left - mTmpRect.left, - transformedRect.top - mTmpRect.top); + setWindowSizeAndCenter((int) transformedRect.width(), (int) transformedRect.height(), + (int) transformedRect.centerX(), (int) transformedRect.centerY()); } /** Returns the rotation degree change of two {@link Surface.Rotation} */ @@ -573,16 +578,52 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } } - private void setMagnificationFrameWith(Rect windowBounds, int centerX, int centerY) { + /** + * Sets the window size with given width and height in pixels without changing the + * window center. The width or the height will be clamped in the range + * [{@link #mMinWindowSize}, screen width or height]. + * + * @param width the window width in pixels + * @param height the window height in pixels. + */ + public void setWindowSize(int width, int height) { + setWindowSizeAndCenter(width, height, Float.NaN, Float.NaN); + } + + void setWindowSizeAndCenter(int width, int height, float centerX, float centerY) { + width = MathUtils.clamp(width, mMinWindowSize, mWindowBounds.width()); + height = MathUtils.clamp(height, mMinWindowSize, mWindowBounds.height()); + + if (Float.isNaN(centerX)) { + centerX = mMagnificationFrame.centerX(); + } + if (Float.isNaN(centerX)) { + centerY = mMagnificationFrame.centerY(); + } + + final int frameWidth = width - 2 * mMirrorSurfaceMargin; + final int frameHeight = height - 2 * mMirrorSurfaceMargin; + setMagnificationFrame(frameWidth, frameHeight, (int) centerX, (int) centerY); + calculateMagnificationFrameBoundary(); + // Correct the frame position to ensure it is inside the boundary. + updateMagnificationFramePosition(0, 0); + modifyWindowMagnification(true); + } + + private void setMagnificationFrame(int width, int height, int centerX, int centerY) { // Sets the initial frame area for the mirror and place it to the given center on the // display. + final int initX = centerX - width / 2; + final int initY = centerY - height / 2; + mMagnificationFrame.set(initX, initY, initX + width, initY + height); + } + + private Size getDefaultWindowSizeWithWindowBounds(Rect windowBounds) { int initSize = Math.min(windowBounds.width(), windowBounds.height()) / 2; initSize = Math.min(mResources.getDimensionPixelSize(R.dimen.magnification_max_frame_size), initSize); initSize += 2 * mMirrorSurfaceMargin; - final int initX = centerX - initSize / 2; - final int initY = centerY - initSize / 2; - mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize); + return new Size(initSize, initSize); } /** @@ -596,8 +637,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } mTransaction.show(mMirrorSurface) .reparent(mMirrorSurface, mMirrorSurfaceView.getSurfaceControl()); - - modifyWindowMagnification(mTransaction); + modifyWindowMagnification(false); } private void addDragTouchListeners() { @@ -615,18 +655,25 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } /** - * Modifies the placement of the mirrored content when the position of mMirrorView is updated. + * Modifies the placement of the mirrored content when the position or size of mMirrorView is + * updated. + * + * @param computeWindowSize set to {@code true} to compute window size with + * {@link #mMagnificationFrame}. */ - private void modifyWindowMagnification(SurfaceControl.Transaction t) { + private void modifyWindowMagnification(boolean computeWindowSize) { mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback); - updateMirrorViewLayout(); + updateMirrorViewLayout(computeWindowSize); } /** - * Updates the layout params of MirrorView and translates MirrorView position when the view is - * moved close to the screen edges. + * Updates the layout params of MirrorView based on the size of {@link #mMagnificationFrame} + * and translates MirrorView position when the view is moved close to the screen edges; + * + * @param computeWindowSize set to {@code true} to compute window size with + * {@link #mMagnificationFrame}. */ - private void updateMirrorViewLayout() { + private void updateMirrorViewLayout(boolean computeWindowSize) { if (!isWindowVisible()) { return; } @@ -637,6 +684,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold (LayoutParams) mMirrorView.getLayoutParams(); params.x = mMagnificationFrame.left - mMirrorSurfaceMargin; params.y = mMagnificationFrame.top - mMirrorSurfaceMargin; + if (computeWindowSize) { + params.width = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin; + params.height = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin; + } // Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView // able to move close to the screen edges. @@ -899,7 +950,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold createMirrorWindow(); showControls(); } else { - modifyWindowMagnification(mTransaction); + modifyWindowMagnification(false); } } @@ -930,7 +981,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return; } if (updateMagnificationFramePosition((int) offsetX, (int) offsetY)) { - modifyWindowMagnification(mTransaction); + modifyWindowMagnification(false); } } @@ -1014,9 +1065,15 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets); pw.println(" mScale:" + mScale); pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty")); + pw.println(" mMagnificationFrameBoundary:" + + (isWindowVisible() ? mMagnificationFrameBoundary : "empty")); + pw.println(" mMagnificationFrame:" + + (isWindowVisible() ? mMagnificationFrame : "empty")); pw.println(" mSourceBounds:" + (isWindowVisible() ? mSourceBounds : "empty")); pw.println(" mSystemGestureTop:" + mSystemGestureTop); + pw.println(" mMagnificationFrameOffsetX:" + mMagnificationFrameOffsetX); + pw.println(" mMagnificationFrameOffsetY:" + mMagnificationFrameOffsetY); } private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate { 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 1dd5e227a909e..6e5926db519d9 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,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; @LargeTest @TestableLooper.RunWithLooper @@ -345,15 +346,17 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void onOrientationChanged_disabled_updateDisplayRotation() { - final Display display = Mockito.spy(mContext.getDisplay()); - when(display.getRotation()).thenReturn(Surface.ROTATION_90); - when(mContext.getDisplay()).thenReturn(display); + final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds()); + // Rotate the window clockwise 90 degree. + windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom, + windowBounds.right); + mWindowManager.setWindowBounds(windowBounds); + final int newRotation = simulateRotateTheDevice(); - mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION); - }); + mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged( + ActivityInfo.CONFIG_ORIENTATION)); - assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation); + assertEquals(newRotation, mWindowMagnificationController.mRotation); } @Test @@ -603,6 +606,113 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag()); } + @Test + public void setMinimumWindowSize_enabled_expectedWindowSize() { + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final int expectedWindowHeight = minimumWindowSize; + final int expectedWindowWidth = minimumWindowSize; + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight); + actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height); + actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width); + + }); + + assertEquals(expectedWindowHeight, actualWindowHeight.get()); + assertEquals(expectedWindowWidth, actualWindowWidth.get()); + } + + @Test + public void setMinimumWindowSizeThenEnable_expectedWindowSize() { + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final int expectedWindowHeight = minimumWindowSize; + final int expectedWindowWidth = minimumWindowSize; + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight); + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN); + actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height); + actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width); + }); + + assertEquals(expectedWindowHeight, actualWindowHeight.get()); + assertEquals(expectedWindowWidth, actualWindowWidth.get()); + } + + @Test + public void setWindowSizeLessThanMin_enabled_minimumWindowSize() { + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(minimumWindowSize - 10, + minimumWindowSize - 10); + actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height); + actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width); + }); + + assertEquals(minimumWindowSize, actualWindowHeight.get()); + assertEquals(minimumWindowSize, actualWindowWidth.get()); + } + + @Test + public void setWindowSizeLargerThanScreenSize_enabled_windowSizeIsScreenSize() { + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(bounds.width() + 10, bounds.height() + 10); + actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height); + actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width); + }); + + assertEquals(bounds.height(), actualWindowHeight.get()); + assertEquals(bounds.width(), actualWindowWidth.get()); + } + + @Test + public void setWindowCenterOutOfScreen_enabled_magnificationCenterIsInsideTheScreen() { + + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger magnificationCenterX = new AtomicInteger(); + final AtomicInteger magnificationCenterY = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSizeAndCenter(minimumWindowSize, + minimumWindowSize, bounds.right, bounds.bottom); + magnificationCenterX.set((int) mWindowMagnificationController.getCenterX()); + magnificationCenterY.set((int) mWindowMagnificationController.getCenterY()); + }); + + assertTrue(magnificationCenterX.get() < bounds.right); + assertTrue(magnificationCenterY.get() < bounds.bottom); + } + private CharSequence getAccessibilityWindowTitle() { final View mirrorView = mWindowManager.getAttachedView(); if (mirrorView == null) {