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

Commit ca70b013 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Add fixed rotation display adjustments

If an activity is launched with fixed rotation transform,
its window layout and configuration will be rotated. But
if it gets real information from the display, the values
(getRotation, getRealSize, getRealMetric, getCutout) are
inconsistent with the actual appearance.

This change provides the basic information to adjust the
returned values.

Bug: 147213487
Test: atest DisplayAdjustmentsTests#testFixedRotationAdjustments
Change-Id: I864d5759f41209d5f93c4a9011b720675c25e765
parent 53ed7078
Loading
Loading
Loading
Loading
+30 −4
Original line number Diff line number Diff line
@@ -103,6 +103,14 @@ public final class Display {
    private int mCachedAppWidthCompat;
    private int mCachedAppHeightCompat;

    /**
     * Indicates that the application is started in a different rotation than the real display, so
     * the display information may be adjusted. That ensures the methods {@link #getRotation},
     * {@link #getRealSize}, {@link #getRealMetrics}, and {@link #getCutout} are consistent with how
     * the application window is laid out.
     */
    private boolean mMayAdjustByFixedRotation;

    /**
     * The default Display id, which is the id of the primary display assuming there is one.
     */
@@ -804,7 +812,9 @@ public final class Display {
    public int getRotation() {
        synchronized (this) {
            updateDisplayInfoLocked();
            return mDisplayInfo.rotation;
            return mMayAdjustByFixedRotation
                    ? getDisplayAdjustments().getRotation(mDisplayInfo.rotation)
                    : mDisplayInfo.rotation;
        }
    }

@@ -828,7 +838,9 @@ public final class Display {
    public DisplayCutout getCutout() {
        synchronized (this) {
            updateDisplayInfoLocked();
            return mDisplayInfo.displayCutout;
            return mMayAdjustByFixedRotation
                    ? getDisplayAdjustments().getDisplayCutout(mDisplayInfo.displayCutout)
                    : mDisplayInfo.displayCutout;
        }
    }

@@ -1140,6 +1152,9 @@ public final class Display {
            updateDisplayInfoLocked();
            outSize.x = mDisplayInfo.logicalWidth;
            outSize.y = mDisplayInfo.logicalHeight;
            if (mMayAdjustByFixedRotation) {
                getDisplayAdjustments().adjustSize(outSize, mDisplayInfo.rotation);
            }
        }
    }

@@ -1159,6 +1174,9 @@ public final class Display {
            updateDisplayInfoLocked();
            mDisplayInfo.getLogicalMetrics(outMetrics,
                    CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
            if (mMayAdjustByFixedRotation) {
                getDisplayAdjustments().adjustMetrics(outMetrics, mDisplayInfo.rotation);
            }
        }
    }

@@ -1225,6 +1243,11 @@ public final class Display {
                }
            }
        }

        // TODO(b/147213487): Replace the condition with per-resources state.
        mMayAdjustByFixedRotation = mIsValid && mResources != null
                && mResources.getConfiguration().windowConfiguration.getRotation()
                != mDisplayInfo.rotation;
    }

    private void updateCachedAppSizeIfNeededLocked() {
@@ -1243,9 +1266,12 @@ public final class Display {
    public String toString() {
        synchronized (this) {
            updateDisplayInfoLocked();
            mDisplayInfo.getAppMetrics(mTempMetrics, getDisplayAdjustments());
            final DisplayAdjustments adjustments = getDisplayAdjustments();
            mDisplayInfo.getAppMetrics(mTempMetrics, adjustments);
            return "Display id " + mDisplayId + ": " + mDisplayInfo
                    + ", " + mTempMetrics + ", isValid=" + mIsValid;
                    + (mMayAdjustByFixedRotation
                            ? (", " + adjustments.getFixedRotationAdjustments() + ", ") : ", ")
                    + mTempMetrics + ", isValid=" + mIsValid;
        }
    }

+150 −2
Original line number Diff line number Diff line
@@ -21,6 +21,10 @@ import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.DisplayMetrics;

import java.util.Objects;

@@ -30,6 +34,7 @@ public class DisplayAdjustments {

    private volatile CompatibilityInfo mCompatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
    private final Configuration mConfiguration = new Configuration(Configuration.EMPTY);
    private FixedRotationAdjustments mFixedRotationAdjustments;

    @UnsupportedAppUsage
    public DisplayAdjustments() {
@@ -44,6 +49,7 @@ public class DisplayAdjustments {
    public DisplayAdjustments(@NonNull DisplayAdjustments daj) {
        setCompatibilityInfo(daj.mCompatInfo);
        mConfiguration.setTo(daj.getConfiguration());
        mFixedRotationAdjustments = daj.mFixedRotationAdjustments;
    }

    @UnsupportedAppUsage
@@ -84,11 +90,78 @@ public class DisplayAdjustments {
        return mConfiguration;
    }

    public void setFixedRotationAdjustments(FixedRotationAdjustments fixedRotationAdjustments) {
        mFixedRotationAdjustments = fixedRotationAdjustments;
    }

    public FixedRotationAdjustments getFixedRotationAdjustments() {
        return mFixedRotationAdjustments;
    }

    /** Returns {@code false} if the width and height of display should swap. */
    private boolean noFlip(@Surface.Rotation int realRotation) {
        final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
        if (rotationAdjustments == null) {
            return true;
        }
        // Check if the delta is rotated by 90 degrees.
        return (realRotation - rotationAdjustments.mRotation + 4) % 2 == 0;
    }

    /** Adjusts the given size if possible. */
    public void adjustSize(@NonNull Point size, @Surface.Rotation int realRotation) {
        if (noFlip(realRotation)) {
            return;
        }
        final int w = size.x;
        size.x = size.y;
        size.y = w;
    }

    /** Adjusts the given metrics if possible. */
    public void adjustMetrics(@NonNull DisplayMetrics metrics, @Surface.Rotation int realRotation) {
        if (noFlip(realRotation)) {
            return;
        }
        int w = metrics.widthPixels;
        metrics.widthPixels = metrics.heightPixels;
        metrics.heightPixels = w;

        w = metrics.noncompatWidthPixels;
        metrics.noncompatWidthPixels = metrics.noncompatHeightPixels;
        metrics.noncompatHeightPixels = w;

        float x = metrics.xdpi;
        metrics.xdpi = metrics.ydpi;
        metrics.ydpi = x;

        x = metrics.noncompatXdpi;
        metrics.noncompatXdpi = metrics.noncompatYdpi;
        metrics.noncompatYdpi = x;
    }

    /** Returns the adjusted cutout if available. Otherwise the original cutout is returned. */
    @Nullable
    public DisplayCutout getDisplayCutout(@Nullable DisplayCutout realCutout) {
        final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
        return rotationAdjustments != null && rotationAdjustments.mRotatedDisplayCutout != null
                ? rotationAdjustments.mRotatedDisplayCutout
                : realCutout;
    }

    /** Returns the adjusted rotation if available. Otherwise the original rotation is returned. */
    @Surface.Rotation
    public int getRotation(@Surface.Rotation int realRotation) {
        final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
        return rotationAdjustments != null ? rotationAdjustments.mRotation : realRotation;
    }

    @Override
    public int hashCode() {
        int hash = 17;
        hash = hash * 31 + Objects.hashCode(mCompatInfo);
        hash = hash * 31 + Objects.hashCode(mConfiguration);
        hash = hash * 31 + Objects.hashCode(mFixedRotationAdjustments);
        return hash;
    }

@@ -98,7 +171,82 @@ public class DisplayAdjustments {
            return false;
        }
        DisplayAdjustments daj = (DisplayAdjustments)o;
        return Objects.equals(daj.mCompatInfo, mCompatInfo) &&
                Objects.equals(daj.mConfiguration, mConfiguration);
        return Objects.equals(daj.mCompatInfo, mCompatInfo)
                && Objects.equals(daj.mConfiguration, mConfiguration)
                && Objects.equals(daj.mFixedRotationAdjustments, mFixedRotationAdjustments);
    }

    /**
     * An application can be launched in different rotation than the real display. This class
     * provides the information to adjust the values returned by {@link #Display}.
     * @hide
     */
    public static class FixedRotationAdjustments implements Parcelable {
        /** The application-based rotation. */
        @Surface.Rotation
        final int mRotation;

        /** Non-null if the device has cutout. */
        @Nullable
        final DisplayCutout mRotatedDisplayCutout;

        public FixedRotationAdjustments(@Surface.Rotation int rotation, DisplayCutout cutout) {
            mRotation = rotation;
            mRotatedDisplayCutout = cutout;
        }

        @Override
        public int hashCode() {
            int hash = 17;
            hash = hash * 31 + mRotation;
            hash = hash * 31 + Objects.hashCode(mRotatedDisplayCutout);
            return hash;
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof FixedRotationAdjustments)) {
                return false;
            }
            final FixedRotationAdjustments other = (FixedRotationAdjustments) o;
            return mRotation == other.mRotation
                    && Objects.equals(mRotatedDisplayCutout, other.mRotatedDisplayCutout);
        }

        @Override
        public String toString() {
            return "FixedRotationAdjustments{rotation=" + Surface.rotationToString(mRotation)
                    + " cutout=" + mRotatedDisplayCutout + "}";
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(mRotation);
            dest.writeTypedObject(
                    new DisplayCutout.ParcelableWrapper(mRotatedDisplayCutout), flags);
        }

        private FixedRotationAdjustments(Parcel in) {
            mRotation = in.readInt();
            final DisplayCutout.ParcelableWrapper cutoutWrapper =
                    in.readTypedObject(DisplayCutout.ParcelableWrapper.CREATOR);
            mRotatedDisplayCutout = cutoutWrapper != null ? cutoutWrapper.get() : null;
        }

        public static final Creator<FixedRotationAdjustments> CREATOR =
                new Creator<FixedRotationAdjustments>() {
            public FixedRotationAdjustments createFromParcel(Parcel in) {
                return new FixedRotationAdjustments(in);
            }

            public FixedRotationAdjustments[] newArray(int size) {
                return new FixedRotationAdjustments[size];
            }
        };
    }
}
+37 −0
Original line number Diff line number Diff line
@@ -19,6 +19,9 @@ package android.view;
import static org.junit.Assert.assertEquals;

import android.content.res.Configuration;
import android.graphics.Point;
import android.util.DisplayMetrics;
import android.view.DisplayAdjustments.FixedRotationAdjustments;

import androidx.test.ext.junit.runners.AndroidJUnit4;

@@ -67,4 +70,38 @@ public class DisplayAdjustmentsTests {

        assertEquals(configuration, newAdjustments.getConfiguration());
    }

    @Test
    public void testFixedRotationAdjustments() {
        final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
        final int realRotation = Surface.ROTATION_0;
        final int fixedRotation = Surface.ROTATION_90;

        mDisplayAdjustments.setFixedRotationAdjustments(
                new FixedRotationAdjustments(fixedRotation, null /* cutout */));

        final int w = 1000;
        final int h = 2000;
        final Point size = new Point(w, h);
        mDisplayAdjustments.adjustSize(size, realRotation);

        assertEquals(fixedRotation, mDisplayAdjustments.getRotation(realRotation));
        assertEquals(new Point(h, w), size);

        final DisplayMetrics metrics = new DisplayMetrics();
        metrics.xdpi = metrics.noncompatXdpi = w;
        metrics.widthPixels = metrics.noncompatWidthPixels = w;
        metrics.ydpi = metrics.noncompatYdpi = h;
        metrics.heightPixels = metrics.noncompatHeightPixels = h;

        final DisplayMetrics flippedMetrics = new DisplayMetrics();
        flippedMetrics.xdpi = flippedMetrics.noncompatXdpi = h;
        flippedMetrics.widthPixels = flippedMetrics.noncompatWidthPixels = h;
        flippedMetrics.ydpi = flippedMetrics.noncompatYdpi = w;
        flippedMetrics.heightPixels = flippedMetrics.noncompatHeightPixels = w;

        mDisplayAdjustments.adjustMetrics(metrics, realRotation);

        assertEquals(flippedMetrics, metrics);
    }
}