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

Commit ed033ac4 authored by Ryan Lin's avatar Ryan Lin Committed by Android (Google) Code Review
Browse files

Merge "Support set window size API" into tm-dev

parents 2c5c3d54 f1dfa438
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -606,6 +606,9 @@
    <!-- The padding ratio of the Accessibility icon foreground drawable -->
    <item name="accessibility_icon_foreground_padding_ratio" type="dimen">21.88%</item>

    <!-- The minimum window size of the accessibility window magnifier -->
    <dimen name="accessibility_window_magnifier_min_size">122dp</dimen>

    <!-- Margin around the various security views -->
    <dimen name="keyguard_muliuser_selector_margin">8dp</dimen>

+1 −0
Original line number Diff line number Diff line
@@ -4399,6 +4399,7 @@
  <java-symbol type="color" name="accessibility_focus_highlight_color" />
  <!-- Width of the outline stroke used by the accessibility focus rectangle -->
  <java-symbol type="dimen" name="accessibility_focus_highlight_stroke_width" />
  <java-symbol type="dimen" name="accessibility_window_magnifier_min_size" />

  <java-symbol type="bool" name="config_attachNavBarToAppDuringTransition" />

+86 −29
Original line number Diff line number Diff line
@@ -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 {
+117 −7
Original line number Diff line number Diff line
@@ -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) {