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

Commit 9639ed2d authored by Jiaming Liu's avatar Jiaming Liu Committed by Automerger Merge Worker
Browse files

Merge "Use rotation from WindowConfiguration to rotate FoldingFeature to avoid...

Merge "Use rotation from WindowConfiguration to rotate FoldingFeature to avoid race condition" into udc-qpr-dev am: 6878c3ab

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/24750813



Change-Id: I872ec3ee99ecbe6062a8062d28a8d8ac4c75a133
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 3cfe5c70 6878c3ab
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package androidx.window.extensions.layout;

import static android.view.Display.DEFAULT_DISPLAY;

import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT;
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED;
import static androidx.window.util.ExtensionHelper.isZero;
@@ -327,13 +326,17 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
            return features;
        }

        // We will transform the feature bounds to the Activity window, so using the rotation
        // from the same source (WindowConfiguration) to make sure they are synchronized.
        final int rotation = windowConfiguration.getDisplayRotation();

        for (CommonFoldingFeature baseFeature : storedFeatures) {
            Integer state = convertToExtensionState(baseFeature.getState());
            if (state == null) {
                continue;
            }
            Rect featureRect = baseFeature.getRect();
            rotateRectToDisplayRotation(displayId, featureRect);
            rotateRectToDisplayRotation(displayId, rotation, featureRect);
            transformToWindowSpaceRect(windowConfiguration, featureRect);

            if (isZero(featureRect)) {
+3 −2
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package androidx.window.sidecar;

import static android.view.Display.DEFAULT_DISPLAY;

import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;

@@ -120,10 +119,12 @@ class SampleSidecarImpl extends StubSidecar {
        }

        List<SidecarDisplayFeature> features = new ArrayList<>();
        final int rotation = activity.getResources().getConfiguration().windowConfiguration
                .getDisplayRotation();
        for (CommonFoldingFeature baseFeature : mStoredFeatures) {
            SidecarDisplayFeature feature = new SidecarDisplayFeature();
            Rect featureRect = baseFeature.getRect();
            rotateRectToDisplayRotation(displayId, featureRect);
            rotateRectToDisplayRotation(displayId, rotation, featureRect);
            transformToWindowSpaceRect(activity, featureRect);
            feature.setRect(featureRect);
            feature.setType(baseFeature.getType());
+32 −39
Original line number Diff line number Diff line
@@ -16,21 +16,22 @@

package androidx.window.util;

import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;

import android.annotation.SuppressLint;
import android.app.WindowConfiguration;
import android.content.Context;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
import android.util.RotationUtils;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.WindowManager;

import androidx.annotation.NonNull;
import androidx.annotation.UiContext;
import androidx.annotation.VisibleForTesting;

/**
 * Util class for both Sidecar and Extensions.
@@ -44,47 +45,39 @@ public final class ExtensionHelper {
    /**
     * Rotates the input rectangle specified in default display orientation to the current display
     * rotation.
     *
     * @param displayId the display id.
     * @param rotation the target rotation relative to the default display orientation.
     * @param inOutRect the input/output Rect as specified in the default display orientation.
     */
    public static void rotateRectToDisplayRotation(int displayId, Rect inOutRect) {
        DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance();
        DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId);
        int rotation = displayInfo.rotation;

        boolean isSideRotation = rotation == ROTATION_90 || rotation == ROTATION_270;
        int displayWidth = isSideRotation ? displayInfo.logicalHeight : displayInfo.logicalWidth;
        int displayHeight = isSideRotation ? displayInfo.logicalWidth : displayInfo.logicalHeight;

        inOutRect.intersect(0, 0, displayWidth, displayHeight);
    public static void rotateRectToDisplayRotation(
            int displayId, @Surface.Rotation int rotation, @NonNull Rect inOutRect) {
        final DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance();
        final DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId);

        rotateBounds(inOutRect, displayWidth, displayHeight, rotation);
        rotateRectToDisplayRotation(displayInfo, rotation, inOutRect);
    }

    /**
     * Rotates the input rectangle within parent bounds for a given delta.
     */
    private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight,
            @Surface.Rotation int delta) {
        int origLeft = inOutRect.left;
        switch (delta) {
            case ROTATION_0:
                return;
            case ROTATION_90:
                inOutRect.left = inOutRect.top;
                inOutRect.top = parentWidth - inOutRect.right;
                inOutRect.right = inOutRect.bottom;
                inOutRect.bottom = parentWidth - origLeft;
                return;
            case ROTATION_180:
                inOutRect.left = parentWidth - inOutRect.right;
                inOutRect.right = parentWidth - origLeft;
                return;
            case ROTATION_270:
                inOutRect.left = parentHeight - inOutRect.bottom;
                inOutRect.bottom = inOutRect.right;
                inOutRect.right = parentHeight - inOutRect.top;
                inOutRect.top = origLeft;
                return;
        }
    // We suppress the Lint error CheckResult for Rect#intersect because in case the displayInfo and
    // folding features are out of sync, e.g. when a foldable devices is unfolding, it is acceptable
    // to provide the original folding feature Rect even if they don't intersect.
    @SuppressLint("RectIntersectReturnValueIgnored")
    @VisibleForTesting
    static void rotateRectToDisplayRotation(@NonNull DisplayInfo displayInfo,
            @Surface.Rotation int rotation, @NonNull Rect inOutRect) {
        // The inOutRect is specified in the default display orientation, so here we need to get
        // the display width and height in the default orientation to perform the intersection and
        // rotation.
        final boolean isSideRotation =
                displayInfo.rotation == ROTATION_90 || displayInfo.rotation == ROTATION_270;
        final int baseDisplayWidth =
                isSideRotation ? displayInfo.logicalHeight : displayInfo.logicalWidth;
        final int baseDisplayHeight =
                isSideRotation ? displayInfo.logicalWidth : displayInfo.logicalHeight;

        inOutRect.intersect(0, 0, baseDisplayWidth, baseDisplayHeight);

        RotationUtils.rotateBounds(inOutRect, baseDisplayWidth, baseDisplayHeight, rotation);
    }

    /** Transforms rectangle from absolute coordinate space to the window coordinate space. */
+1 −1
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Test class for {@link WindowExtensionsTest}.
 * Test class for {@link WindowExtensions}.
 *
 * Build/Install/Run:
 *  atest WMJetpackUnitTests:WindowExtensionsTest
+119 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.window.util;

import static org.junit.Assert.assertEquals;

import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
import android.view.Surface;

import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Test class for {@link ExtensionHelper}.
 *
 * Build/Install/Run:
 *  atest WMJetpackUnitTests:ExtensionHelperTest
 */
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ExtensionHelperTest {

    private static final int MOCK_DISPLAY_HEIGHT = 1000;
    private static final int MOCK_DISPLAY_WIDTH = 2000;
    private static final int MOCK_FEATURE_LEFT = 100;
    private static final int MOCK_FEATURE_RIGHT = 200;

    private static final int[] ROTATIONS = {
            Surface.ROTATION_0,
            Surface.ROTATION_90,
            Surface.ROTATION_180,
            Surface.ROTATION_270
    };

    private static final DisplayInfo[] MOCK_DISPLAY_INFOS = {
            getMockDisplayInfo(Surface.ROTATION_0),
            getMockDisplayInfo(Surface.ROTATION_90),
            getMockDisplayInfo(Surface.ROTATION_180),
            getMockDisplayInfo(Surface.ROTATION_270),
    };

    @Test
    public void testRotateRectToDisplayRotation() {
        for (int rotation : ROTATIONS) {
            final Rect expectedResult = getExpectedFeatureRectAfterRotation(rotation);
            // The method should return correctly rotated Rect even if the requested rotation value
            // differs from the rotation in DisplayInfo. This is because the WindowConfiguration is
            // not always synced with DisplayInfo.
            for (DisplayInfo displayInfo : MOCK_DISPLAY_INFOS) {
                final Rect rect = getMockFeatureRect();
                ExtensionHelper.rotateRectToDisplayRotation(displayInfo, rotation, rect);
                assertEquals(
                        "Result Rect should equal to expected for rotation: " + rotation
                                + "; displayInfo: " + displayInfo,
                        expectedResult, rect);
            }
        }
    }

    @NonNull
    private static DisplayInfo getMockDisplayInfo(@Surface.Rotation int rotation) {
        final DisplayInfo displayInfo = new DisplayInfo();
        displayInfo.rotation = rotation;
        if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
            displayInfo.logicalWidth = MOCK_DISPLAY_WIDTH;
            displayInfo.logicalHeight = MOCK_DISPLAY_HEIGHT;
        } else {
            displayInfo.logicalWidth = MOCK_DISPLAY_HEIGHT;
            displayInfo.logicalHeight = MOCK_DISPLAY_WIDTH;
        }
        return displayInfo;
    }

    @NonNull
    private static Rect getMockFeatureRect() {
        return new Rect(MOCK_FEATURE_LEFT, 0, MOCK_FEATURE_RIGHT, MOCK_DISPLAY_HEIGHT);
    }

    @NonNull
    private static Rect getExpectedFeatureRectAfterRotation(@Surface.Rotation int rotation) {
        switch (rotation) {
            case Surface.ROTATION_0:
                return new Rect(
                        MOCK_FEATURE_LEFT, 0, MOCK_FEATURE_RIGHT, MOCK_DISPLAY_HEIGHT);
            case Surface.ROTATION_90:
                return new Rect(0, MOCK_DISPLAY_WIDTH - MOCK_FEATURE_RIGHT,
                        MOCK_DISPLAY_HEIGHT, MOCK_DISPLAY_WIDTH - MOCK_FEATURE_LEFT);
            case Surface.ROTATION_180:
                return new Rect(MOCK_DISPLAY_WIDTH - MOCK_FEATURE_RIGHT, 0,
                        MOCK_DISPLAY_WIDTH - MOCK_FEATURE_LEFT, MOCK_DISPLAY_HEIGHT);
            case Surface.ROTATION_270:
                return new Rect(0, MOCK_FEATURE_LEFT, MOCK_DISPLAY_HEIGHT,
                        MOCK_FEATURE_RIGHT);
            default:
                throw new IllegalArgumentException("Unknown rotation value: " + rotation);
        }
    }
}