diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 717c7156f9174..a51e3fcfd50b7 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -28,6 +28,7 @@ import android.annotation.Nullable; import android.annotation.UiContext; import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Insets; import android.graphics.Matrix; @@ -35,6 +36,7 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; @@ -76,6 +78,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold MirrorWindowControl.MirrorWindowDelegate, MagnificationGestureDetector.OnGestureListener { private static final String TAG = "WindowMagnificationController"; + @SuppressWarnings("isloggabletaglength") + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Build.IS_DEBUGGABLE; // Delay to avoid updating state description too frequently. private static final int UPDATE_STATE_DESCRIPTION_DELAY_MS = 100; // It should be consistent with the value defined in WindowMagnificationGestureHandler. @@ -158,14 +162,15 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mRotation = display.getRotation(); mWm = context.getSystemService(WindowManager.class); - mWindowBounds = mWm.getCurrentWindowMetrics().getBounds(); + mWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds()); mResources = mContext.getResources(); mScale = mResources.getInteger(R.integer.magnification_default_scale); mBounceEffectDuration = mResources.getInteger( com.android.internal.R.integer.config_shortAnimTime); updateDimensions(); - setInitialStartBounds(); + setMagnificationFrameWith(mWindowBounds, mWindowBounds.width() / 2, + mWindowBounds.height() / 2); computeBounceAnimationScale(); mMirrorWindowControl = mirrorWindowControl; @@ -286,18 +291,59 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold * @param configDiff a bit mask of the differences between the configurations */ void onConfigurationChanged(int configDiff) { + if (DEBUG) { + Log.d(TAG, "onConfigurationChanged = " + Configuration.configurationDiffToString( + configDiff)); + } + if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) { + onRotate(); + } + + if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) { + updateAccessibilityWindowTitleIfNeeded(); + } + + boolean reCreateWindow = false; if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) { updateDimensions(); computeBounceAnimationScale(); - if (isWindowVisible()) { - deleteWindowMagnification(); - enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN); - } - } else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) { - onRotate(); - } else if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) { - updateAccessibilityWindowTitleIfNeeded(); + reCreateWindow = true; } + + if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) { + reCreateWindow |= handleScreenSizeChanged(); + } + + // Recreate the window again to correct the window appearance due to density or + // window size changed not caused by rotation. + if (isWindowVisible() && reCreateWindow) { + deleteWindowMagnification(); + enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN); + } + } + + /** + * Calculates the magnification frame if the window bounds is changed. + * Note that the orientation also changes the wind bounds, so it should be handled first. + * + * @return {@code true} if the magnification frame is changed with the new window bounds. + */ + private boolean handleScreenSizeChanged() { + final Rect oldWindowBounds = new Rect(mWindowBounds); + final Rect currentWindowBounds = mWm.getCurrentWindowMetrics().getBounds(); + + if (currentWindowBounds.equals(oldWindowBounds)) { + if (DEBUG) { + Log.d(TAG, "updateMagnificationFrame -- window bounds is not changed"); + } + return false; + } + mWindowBounds.set(currentWindowBounds); + final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width(); + final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height(); + setMagnificationFrameWith(mWindowBounds, (int) newCenterX, (int) newCenterY); + calculateMagnificationFrameBoundary(); + return true; } private void updateSystemUIStateIfNeeded() { @@ -311,30 +357,42 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mWm.updateViewLayout(mMirrorView, params); } - /** Handles MirrorWindow position when the device rotation changed. */ + /** + * Keep MirrorWindow position on the screen unchanged when device rotates 90° clockwise or + * anti-clockwise. + */ private void onRotate() { final Display display = mContext.getDisplay(); final int oldRotation = mRotation; - mWindowBounds = mWm.getCurrentWindowMetrics().getBounds(); - - setMagnificationFrameBoundary(); mRotation = display.getRotation(); + final int rotationDegree = getDegreeFromRotation(mRotation, oldRotation); + if (rotationDegree == 0 || rotationDegree == 180) { + Log.w(TAG, "onRotate -- rotate with the device. skip it"); + return; + } + final Rect currentWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds()); + if (currentWindowBounds.width() != mWindowBounds.height() + || currentWindowBounds.height() != mWindowBounds.width()) { + Log.w(TAG, "onRotate -- unexpected window height/width"); + return; + } + + mWindowBounds.set(currentWindowBounds); + + calculateMagnificationFrameBoundary(); if (!isWindowVisible()) { return; } // Keep MirrorWindow position on the screen unchanged when device rotates 90° // clockwise or anti-clockwise. - final int rotationDegree = getDegreeFromRotation(mRotation, oldRotation); + final Matrix matrix = new Matrix(); matrix.setRotate(rotationDegree); if (rotationDegree == 90) { matrix.postTranslate(mWindowBounds.width(), 0); } else if (rotationDegree == 270) { matrix.postTranslate(0, mWindowBounds.height()); - } else { - Log.w(TAG, "Invalid rotation change. " + rotationDegree); - return; } // The rect of MirrorView is going to be transformed. LayoutParams params = @@ -440,12 +498,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } } - private void setInitialStartBounds() { + private void setMagnificationFrameWith(Rect windowBounds, int centerX, int centerY) { // Sets the initial frame area for the mirror and places it in the center of the display. - final int initSize = Math.min(mWindowBounds.width(), mWindowBounds.height()) / 2 + final int initSize = Math.min(windowBounds.width(), windowBounds.height()) / 2 + 2 * mMirrorSurfaceMargin; - final int initX = mWindowBounds.width() / 2 - initSize / 2; - final int initY = mWindowBounds.height() / 2 - initSize / 2; + final int initX = centerX - initSize / 2; + final int initY = centerY - initSize / 2; mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize); } @@ -553,7 +611,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mSourceBounds.set(left, top, right, bottom); } - private void setMagnificationFrameBoundary() { + private void calculateMagnificationFrameBoundary() { // Calculates width and height for magnification frame could exceed out the screen. // TODO : re-calculating again when scale is changed. // The half width of magnification frame. @@ -644,7 +702,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold : centerY - mMagnificationFrame.exactCenterY(); mScale = Float.isNaN(scale) ? mScale : scale; - setMagnificationFrameBoundary(); + calculateMagnificationFrameBoundary(); updateMagnificationFramePosition((int) offsetX, (int) offsetY); if (!isWindowVisible()) { createMirrorWindow(); @@ -764,6 +822,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets); pw.println(" mScale:" + mScale); pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty")); + pw.println(" mSourceBounds:" + + (isWindowVisible() ? mSourceBounds : "empty")); pw.println(" mSystemGestureTop:" + mSystemGestureTop); } 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 9621bedd92100..8bb9d423fa921 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java @@ -16,9 +16,6 @@ package com.android.systemui.accessibility; -import static android.view.WindowInsets.Type.systemGestures; - -import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; import android.view.Display; @@ -79,14 +76,11 @@ public class TestableWindowManager implements WindowManager { @Override public WindowMetrics getCurrentWindowMetrics() { - final Insets systemGesturesInsets = Insets.of(0, 0, 0, 10); - final WindowInsets insets = new WindowInsets.Builder() - .setInsets(systemGestures(), systemGesturesInsets) - .build(); + final WindowMetrics realMetrics = mWindowManager.getCurrentWindowMetrics(); final WindowMetrics windowMetrics = new WindowMetrics( - mWindowBounds == null ? mWindowManager.getCurrentWindowMetrics().getBounds() + mWindowBounds == null ? realMetrics.getBounds() : mWindowBounds, - mWindowInsets == null ? insets : mWindowInsets); + mWindowInsets == null ? realMetrics.getWindowInsets() : mWindowInsets); return windowMetrics; } @@ -106,10 +100,20 @@ public class TestableWindowManager implements WindowManager { return (WindowManager.LayoutParams) mView.getLayoutParams(); } + /** + * Sets the given window bounds to current window metrics. + * + * @param bounds the window bounds + */ public void setWindowBounds(Rect bounds) { mWindowBounds = bounds; } + /** + * Sets the given window insets to the current window metics. + * + * @param insets the window insets. + */ public void setWindowInsets(WindowInsets insets) { mWindowInsets = insets; } 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 f62069d35d350..76cc7a0eda133 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -17,6 +17,7 @@ package com.android.systemui.accessibility; import static android.view.Choreographer.FrameCallback; +import static android.view.WindowInsets.Type.systemGestures; import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP; @@ -45,6 +46,8 @@ import android.app.Instrumentation; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Resources; +import android.graphics.Insets; +import android.graphics.PointF; import android.graphics.Rect; import android.os.Handler; import android.os.SystemClock; @@ -55,6 +58,7 @@ import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; import android.view.View; +import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; @@ -239,11 +243,13 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { mInstrumentation.runOnMainSync(() -> { mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY); mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION); + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE); + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE); }); } @Test - public void onOrientationChanged_enabled_updateDisplayRotationAndLayout() { + public void onOrientationChanged_enabled_updateDisplayRotationAndCenterStayAtSamePosition() { final Display display = Mockito.spy(mContext.getDisplay()); when(display.getRotation()).thenReturn(Surface.ROTATION_90); when(mContext.getDisplay()).thenReturn(display); @@ -251,13 +257,22 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN); }); + final PointF expectedCenter = new PointF(mWindowMagnificationController.getCenterY(), + mWindowMagnificationController.getCenterX()); + final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds()); + // Rotate the window 90 degrees. + windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom, + windowBounds.right); + mWindowManager.setWindowBounds(windowBounds); mInstrumentation.runOnMainSync(() -> { mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION); }); assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation); - // The first invocation is called when the surface is created. + final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(), + mWindowMagnificationController.getCenterY()); + assertEquals(expectedCenter, actualCenter); verify(mWindowManager, times(2)).updateViewLayout(any(), any()); } @@ -274,6 +289,33 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation); } + @Test + public void onScreenSizeChanged_enabledAtTheCenterOfScreen_keepSameWindowSizeRatio() { + // The default position is at the center of the screen. + final float expectedRatio = 0.5f; + final Rect testWindowBounds = new Rect( + mWindowManager.getCurrentWindowMetrics().getBounds()); + testWindowBounds.set(testWindowBounds.left, testWindowBounds.top, + testWindowBounds.right + 100, testWindowBounds.bottom + 100); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN); + }); + mWindowManager.setWindowBounds(testWindowBounds); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE); + }); + + // The ratio of center to window size should be the same. + assertEquals(expectedRatio, + mWindowMagnificationController.getCenterX() / testWindowBounds.width(), + 0); + assertEquals(expectedRatio, + mWindowMagnificationController.getCenterY() / testWindowBounds.height(), + 0); + } + @Test public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() { mInstrumentation.runOnMainSync(() -> { @@ -419,9 +461,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { } @Test - public void moveWindowMagnificationToTheBottom_enabled_overlapFlagIsTrue() { - final WindowManager wm = mContext.getSystemService(WindowManager.class); - final Rect bounds = wm.getCurrentWindowMetrics().getBounds(); + public void moveWindowMagnificationToTheBottom_enabledWithGestureInset_overlapFlagIsTrue() { + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + final WindowInsets testInsets = new WindowInsets.Builder() + .setInsets(systemGestures(), Insets.of(0, 0, 0, 10)) + .build(); + mWindowManager.setWindowInsets(testInsets); mInstrumentation.runOnMainSync(() -> { mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN);