Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 430b258b authored by ryanlwlin's avatar ryanlwlin
Browse files

Fix incorrect boundary/appearance after folding the device

When the device is folded, the screen size is changed.
We need to compute the magnification frame and its boundary
based on current window metrics. Besides, to keep
the magnified center, we rescale the former center and apply it
to the center of the new magnification frame.

Folding the device also triggers orietation changes, which is
the unexpected case. To avoid handling this case, we also check
whethe it is the window hieght/width  exchanging case.

Bug: 185728667
Bug: 185536537
Bug: 180054792
Test: atest WindowMagnificationControllerTest
      manually test with follow cases
      1. change display size or font size
      2. orientate the device
Change-Id: If2a93ad14bf8e07cf149180e639b35fc0bedfd10
parent ba2d2a3d
Loading
Loading
Loading
Loading
+83 −23
Original line number Diff line number Diff line
@@ -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()) {
            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);
        }
        } else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
            onRotate();
        } else if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
            updateAccessibilityWindowTitleIfNeeded();
    }

    /**
     * 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);
    }

+13 −9
Original line number Diff line number Diff line
@@ -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;
    }
+50 −5
Original line number Diff line number Diff line
@@ -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);