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

Commit 98f87e7d authored by Tiger's avatar Tiger
Browse files

Add mSideHint to InsetsSource

When calculating insets, if the relative frame is fully covered by the
insets source frame, we provided the insets to the top side previously.
That could be an unexpected side. For example, IME might expect the
navigation bar at the bottom as well as the IME caption bar. If IME
receives navigation bar insets from the opposite side, IME might add
wrong paddings to the top, and then cause the layout unstable.

This CLs introduces a new field: InsetsSource#mSideHint. It is used to
decide which side of the relative frame should receive insets when the
frame fully covers the relative frame. It is based on the insets
provided to the containing bounds.

This CL also refactors the InternalInsetsSide:
- It is moved from InsetsState to InsetsSource because InsetsState is
  like a container of InsetsSource, so InsetsState doesn't have to be
  known by InsetsSource.
- The prefix "ISIDE_" are renamed to "SIDE_" by dropping the "I".
- ISIDE_FLOATING is renamed to SIDE_NONE to reflect the truth.
- Rearrange the value of each constant.

Fix: 306114031
Bug: 320325449
Test: InsetsStateTest SizeCompatTests WindowInsetsAnimationImeTests
Change-Id: I7210fc38b70742ca2e45810641ab44c62dc589c8
parent e53175bc
Loading
Loading
Loading
Loading
+19 −18
Original line number Diff line number Diff line
@@ -34,11 +34,11 @@ import static android.view.InsetsController.DEBUG;
import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
import static android.view.InsetsController.LayoutInsetsDuringAnimation;
import static android.view.InsetsSource.ID_IME;
import static android.view.InsetsState.ISIDE_BOTTOM;
import static android.view.InsetsState.ISIDE_FLOATING;
import static android.view.InsetsState.ISIDE_LEFT;
import static android.view.InsetsState.ISIDE_RIGHT;
import static android.view.InsetsState.ISIDE_TOP;
import static android.view.InsetsSource.SIDE_BOTTOM;
import static android.view.InsetsSource.SIDE_NONE;
import static android.view.InsetsSource.SIDE_LEFT;
import static android.view.InsetsSource.SIDE_RIGHT;
import static android.view.InsetsSource.SIDE_TOP;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -60,7 +60,7 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseSetArray;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsSide;
import android.view.InsetsSource.InternalInsetsSide;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation.Bounds;
@@ -142,7 +142,7 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
            if (mHasZeroInsetsIme) {
                // IME has shownInsets of ZERO, and can't map to a side by default.
                // Map zero insets IME to bottom, making it a special case of bottom insets.
                idSideMap.put(ID_IME, ISIDE_BOTTOM);
                idSideMap.put(ID_IME, SIDE_BOTTOM);
            }
            buildSideControlsMap(idSideMap, mSideControlsMap, controls);
        } else {
@@ -286,10 +286,10 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
        }
        final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
        final ArrayList<SurfaceParams> params = new ArrayList<>();
        updateLeashesForSide(ISIDE_LEFT, offset.left, params, outState, mPendingAlpha);
        updateLeashesForSide(ISIDE_TOP, offset.top, params, outState, mPendingAlpha);
        updateLeashesForSide(ISIDE_RIGHT, offset.right, params, outState, mPendingAlpha);
        updateLeashesForSide(ISIDE_BOTTOM, offset.bottom, params, outState, mPendingAlpha);
        updateLeashesForSide(SIDE_LEFT, offset.left, params, outState, mPendingAlpha);
        updateLeashesForSide(SIDE_TOP, offset.top, params, outState, mPendingAlpha);
        updateLeashesForSide(SIDE_RIGHT, offset.right, params, outState, mPendingAlpha);
        updateLeashesForSide(SIDE_BOTTOM, offset.bottom, params, outState, mPendingAlpha);

        mController.applySurfaceParams(params.toArray(new SurfaceParams[params.size()]));
        mCurrentInsets = mPendingInsets;
@@ -499,19 +499,19 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
        final float surfaceOffset = mTranslator != null
                ? mTranslator.translateLengthInAppWindowToScreen(offset) : offset;
        switch (side) {
            case ISIDE_LEFT:
            case SIDE_LEFT:
                m.postTranslate(-surfaceOffset, 0);
                frame.offset(-offset, 0);
                break;
            case ISIDE_TOP:
            case SIDE_TOP:
                m.postTranslate(0, -surfaceOffset);
                frame.offset(0, -offset);
                break;
            case ISIDE_RIGHT:
            case SIDE_RIGHT:
                m.postTranslate(surfaceOffset, 0);
                frame.offset(offset, 0);
                break;
            case ISIDE_BOTTOM:
            case SIDE_BOTTOM:
                m.postTranslate(0, surfaceOffset);
                frame.offset(0, offset);
                break;
@@ -543,9 +543,10 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
                // control may be null if it got revoked.
                continue;
            }
            @InternalInsetsSide int side = InsetsState.getInsetSide(control.getInsetsHint());
            if (side == ISIDE_FLOATING && control.getType() == WindowInsets.Type.ime()) {
                side = ISIDE_BOTTOM;
            @InternalInsetsSide int side = InsetsSource.getInsetSide(control.getInsetsHint());
            if (side == SIDE_NONE && control.getType() == WindowInsets.Type.ime()) {
                // IME might not provide insets when it is fullscreen or floating.
                side = SIDE_BOTTOM;
            }
            sideControlsMap.add(side, control);
        }
+103 −8
Original line number Diff line number Diff line
@@ -46,6 +46,24 @@ import java.util.StringJoiner;
 */
public class InsetsSource implements Parcelable {

    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = "SIDE_", value = {
            SIDE_NONE,
            SIDE_LEFT,
            SIDE_TOP,
            SIDE_RIGHT,
            SIDE_BOTTOM,
            SIDE_UNKNOWN
    })
    public @interface InternalInsetsSide {}

    static final int SIDE_NONE = 0;
    static final int SIDE_LEFT = 1;
    static final int SIDE_TOP = 2;
    static final int SIDE_RIGHT = 3;
    static final int SIDE_BOTTOM = 4;
    static final int SIDE_UNKNOWN = 5;

    /** The insets source ID of IME */
    public static final int ID_IME = createId(null, 0, ime());

@@ -101,6 +119,12 @@ public class InsetsSource implements Parcelable {

    private boolean mVisible;

    /**
     * Used to decide which side of the relative frame should receive insets when the frame fully
     * covers the relative frame.
     */
    private @InternalInsetsSide int mSideHint = SIDE_NONE;

    private final Rect mTmpFrame = new Rect();

    public InsetsSource(int id, @InsetsType int type) {
@@ -119,6 +143,7 @@ public class InsetsSource implements Parcelable {
                ? new Rect(other.mVisibleFrame)
                : null;
        mFlags = other.mFlags;
        mSideHint = other.mSideHint;
    }

    public void set(InsetsSource other) {
@@ -128,6 +153,7 @@ public class InsetsSource implements Parcelable {
                ? new Rect(other.mVisibleFrame)
                : null;
        mFlags = other.mFlags;
        mSideHint = other.mSideHint;
    }

    public InsetsSource setFrame(int left, int top, int right, int bottom) {
@@ -160,6 +186,18 @@ public class InsetsSource implements Parcelable {
        return this;
    }

    /**
     * Updates the side hint which is used to decide which side of the relative frame should receive
     * insets when the frame fully covers the relative frame.
     *
     * @param bounds A rectangle which contains the frame. It will be used to calculate the hint.
     */
    public InsetsSource updateSideHint(Rect bounds) {
        mSideHint = getInsetSide(
                calculateInsets(bounds, mFrame, true /* ignoreVisibility */));
        return this;
    }

    public int getId() {
        return mId;
    }
@@ -236,8 +274,21 @@ public class InsetsSource implements Parcelable {
            return Insets.of(0, 0, 0, mTmpFrame.height());
        }

        if (mTmpFrame.equals(relativeFrame)) {
            // Covering all sides
            switch (mSideHint) {
                default:
                case SIDE_LEFT:
                    return Insets.of(mTmpFrame.width(), 0, 0, 0);
                case SIDE_TOP:
                    return Insets.of(0, mTmpFrame.height(), 0, 0);
                case SIDE_RIGHT:
                    return Insets.of(0, 0, mTmpFrame.width(), 0);
                case SIDE_BOTTOM:
                    return Insets.of(0, 0, 0, mTmpFrame.height());
            }
        } else if (mTmpFrame.width() == relativeFrame.width()) {
            // Intersecting at top/bottom
        if (mTmpFrame.width() == relativeFrame.width()) {
            if (mTmpFrame.top == relativeFrame.top) {
                return Insets.of(0, mTmpFrame.height(), 0, 0);
            } else if (mTmpFrame.bottom == relativeFrame.bottom) {
@@ -249,9 +300,8 @@ public class InsetsSource implements Parcelable {
            if (mTmpFrame.top == 0) {
                return Insets.of(0, mTmpFrame.height(), 0, 0);
            }
        }
        } else if (mTmpFrame.height() == relativeFrame.height()) {
            // Intersecting at left/right
        else if (mTmpFrame.height() == relativeFrame.height()) {
            if (mTmpFrame.left == relativeFrame.left) {
                return Insets.of(mTmpFrame.width(), 0, 0, 0);
            } else if (mTmpFrame.right == relativeFrame.right) {
@@ -282,6 +332,46 @@ public class InsetsSource implements Parcelable {
        return false;
    }

    /**
     * Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b
     * is set in order that this method returns a meaningful result.
     */
    static @InternalInsetsSide int getInsetSide(Insets insets) {
        if (Insets.NONE.equals(insets)) {
            return SIDE_NONE;
        }
        if (insets.left != 0) {
            return SIDE_LEFT;
        }
        if (insets.top != 0) {
            return SIDE_TOP;
        }
        if (insets.right != 0) {
            return SIDE_RIGHT;
        }
        if (insets.bottom != 0) {
            return SIDE_BOTTOM;
        }
        return SIDE_UNKNOWN;
    }

    static String sideToString(@InternalInsetsSide int side) {
        switch (side) {
            case SIDE_NONE:
                return "NONE";
            case SIDE_LEFT:
                return "LEFT";
            case SIDE_TOP:
                return "TOP";
            case SIDE_RIGHT:
                return "RIGHT";
            case SIDE_BOTTOM:
                return "BOTTOM";
            default:
                return "UNKNOWN:" + side;
        }
    }

    /**
     * Creates an identifier of an {@link InsetsSource}.
     *
@@ -331,7 +421,7 @@ public class InsetsSource implements Parcelable {
    }

    public static String flagsToString(@Flags int flags) {
        final StringJoiner joiner = new StringJoiner(" ");
        final StringJoiner joiner = new StringJoiner("|");
        if ((flags & FLAG_SUPPRESS_SCRIM) != 0) {
            joiner.add("SUPPRESS_SCRIM");
        }
@@ -371,6 +461,7 @@ public class InsetsSource implements Parcelable {
        }
        pw.print(" visible="); pw.print(mVisible);
        pw.print(" flags="); pw.print(flagsToString(mFlags));
        pw.print(" sideHint="); pw.print(sideToString(mSideHint));
        pw.println();
    }

@@ -393,6 +484,7 @@ public class InsetsSource implements Parcelable {
        if (mType != that.mType) return false;
        if (mVisible != that.mVisible) return false;
        if (mFlags != that.mFlags) return false;
        if (mSideHint != that.mSideHint) return false;
        if (excludeInvisibleImeFrames && !mVisible && mType == WindowInsets.Type.ime()) return true;
        if (!Objects.equals(mVisibleFrame, that.mVisibleFrame)) return false;
        return mFrame.equals(that.mFrame);
@@ -400,7 +492,7 @@ public class InsetsSource implements Parcelable {

    @Override
    public int hashCode() {
        return Objects.hash(mId, mType, mFrame, mVisibleFrame, mVisible, mFlags);
        return Objects.hash(mId, mType, mFrame, mVisibleFrame, mVisible, mFlags, mSideHint);
    }

    public InsetsSource(Parcel in) {
@@ -414,6 +506,7 @@ public class InsetsSource implements Parcelable {
        }
        mVisible = in.readBoolean();
        mFlags = in.readInt();
        mSideHint = in.readInt();
    }

    @Override
@@ -434,6 +527,7 @@ public class InsetsSource implements Parcelable {
        }
        dest.writeBoolean(mVisible);
        dest.writeInt(mFlags);
        dest.writeInt(mSideHint);
    }

    @Override
@@ -442,7 +536,8 @@ public class InsetsSource implements Parcelable {
                + " mType=" + WindowInsets.Type.toString(mType)
                + " mFrame=" + mFrame.toShortString()
                + " mVisible=" + mVisible
                + " mFlags=[" + flagsToString(mFlags) + "]"
                + " mFlags=" + flagsToString(mFlags)
                + " mSideHint=" + sideToString(mSideHint)
                + "}";
    }

+3 −45
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration.ActivityType;
@@ -48,6 +47,7 @@ import android.os.Parcelable;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsSource.InternalInsetsSide;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
@@ -55,8 +55,6 @@ import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import com.android.internal.annotations.VisibleForTesting;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
import java.util.StringJoiner;

@@ -66,23 +64,6 @@ import java.util.StringJoiner;
 */
public class InsetsState implements Parcelable {

    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = "ISIDE", value = {
            ISIDE_LEFT,
            ISIDE_TOP,
            ISIDE_RIGHT,
            ISIDE_BOTTOM,
            ISIDE_FLOATING,
            ISIDE_UNKNOWN
    })
    public @interface InternalInsetsSide {}
    static final int ISIDE_LEFT = 0;
    static final int ISIDE_TOP = 1;
    static final int ISIDE_RIGHT = 2;
    static final int ISIDE_BOTTOM = 3;
    static final int ISIDE_FLOATING = 4;
    static final int ISIDE_UNKNOWN = 5;

    private final SparseArray<InsetsSource> mSources;

    /**
@@ -398,36 +379,13 @@ public class InsetsState implements Parcelable {
        }

        if (idSideMap != null) {
            @InternalInsetsSide int insetSide = getInsetSide(insets);
            if (insetSide != ISIDE_UNKNOWN) {
            @InternalInsetsSide int insetSide = InsetsSource.getInsetSide(insets);
            if (insetSide != InsetsSource.SIDE_UNKNOWN) {
                idSideMap.put(source.getId(), insetSide);
            }
        }
    }

    /**
     * Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b
     * is set in order that this method returns a meaningful result.
     */
    static @InternalInsetsSide int getInsetSide(Insets insets) {
        if (Insets.NONE.equals(insets)) {
            return ISIDE_FLOATING;
        }
        if (insets.left != 0) {
            return ISIDE_LEFT;
        }
        if (insets.top != 0) {
            return ISIDE_TOP;
        }
        if (insets.right != 0) {
            return ISIDE_RIGHT;
        }
        if (insets.bottom != 0) {
            return ISIDE_BOTTOM;
        }
        return ISIDE_UNKNOWN;
    }

    /**
     * Gets the source mapped from the ID, or creates one if no such mapping has been made.
     */
+4 −4
Original line number Diff line number Diff line
@@ -20,8 +20,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.ID_IME;
import static android.view.InsetsState.ISIDE_BOTTOM;
import static android.view.InsetsState.ISIDE_TOP;
import static android.view.InsetsSource.SIDE_BOTTOM;
import static android.view.InsetsSource.SIDE_TOP;
import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
import static android.view.RoundedCorner.POSITION_TOP_LEFT;
@@ -106,8 +106,8 @@ public class InsetsStateTest {
                typeSideMap);
        assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
        assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
        assertEquals(ISIDE_TOP, typeSideMap.get(ID_STATUS_BAR));
        assertEquals(ISIDE_BOTTOM, typeSideMap.get(ID_IME));
        assertEquals(SIDE_TOP, typeSideMap.get(ID_STATUS_BAR));
        assertEquals(SIDE_BOTTOM, typeSideMap.get(ID_IME));
        assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
        assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(ime()));
    }
+19 −15
Original line number Diff line number Diff line
@@ -92,35 +92,39 @@ public class DisplayFrames {
        mRotation = rotation;
        mWidth = w;
        mHeight = h;
        final Rect unrestricted = mUnrestricted;
        unrestricted.set(0, 0, w, h);
        state.setDisplayFrame(unrestricted);
        final Rect u = mUnrestricted;
        u.set(0, 0, w, h);
        state.setDisplayFrame(u);
        state.setDisplayCutout(displayCutout);
        state.setRoundedCorners(roundedCorners);
        state.setPrivacyIndicatorBounds(indicatorBounds);
        state.setDisplayShape(displayShape);
        state.getDisplayCutoutSafe(safe);
        if (safe.left > unrestricted.left) {
            state.getOrCreateSource(ID_DISPLAY_CUTOUT_LEFT, displayCutout()).setFrame(
                    unrestricted.left, unrestricted.top, safe.left, unrestricted.bottom);
        if (safe.left > u.left) {
            state.getOrCreateSource(ID_DISPLAY_CUTOUT_LEFT, displayCutout())
                    .setFrame(u.left, u.top, safe.left, u.bottom)
                    .updateSideHint(u);
        } else {
            state.removeSource(ID_DISPLAY_CUTOUT_LEFT);
        }
        if (safe.top > unrestricted.top) {
            state.getOrCreateSource(ID_DISPLAY_CUTOUT_TOP, displayCutout()).setFrame(
                    unrestricted.left, unrestricted.top, unrestricted.right, safe.top);
        if (safe.top > u.top) {
            state.getOrCreateSource(ID_DISPLAY_CUTOUT_TOP, displayCutout())
                    .setFrame(u.left, u.top, u.right, safe.top)
                    .updateSideHint(u);
        } else {
            state.removeSource(ID_DISPLAY_CUTOUT_TOP);
        }
        if (safe.right < unrestricted.right) {
            state.getOrCreateSource(ID_DISPLAY_CUTOUT_RIGHT, displayCutout()).setFrame(
                    safe.right, unrestricted.top, unrestricted.right, unrestricted.bottom);
        if (safe.right < u.right) {
            state.getOrCreateSource(ID_DISPLAY_CUTOUT_RIGHT, displayCutout())
                    .setFrame(safe.right, u.top, u.right, u.bottom)
                    .updateSideHint(u);
        } else {
            state.removeSource(ID_DISPLAY_CUTOUT_RIGHT);
        }
        if (safe.bottom < unrestricted.bottom) {
            state.getOrCreateSource(ID_DISPLAY_CUTOUT_BOTTOM, displayCutout()).setFrame(
                    unrestricted.left, safe.bottom, unrestricted.right, unrestricted.bottom);
        if (safe.bottom < u.bottom) {
            state.getOrCreateSource(ID_DISPLAY_CUTOUT_BOTTOM, displayCutout())
                    .setFrame(u.left, safe.bottom, u.right, u.bottom)
                    .updateSideHint(u);
        } else {
            state.removeSource(ID_DISPLAY_CUTOUT_BOTTOM);
        }
Loading