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

Commit bcf99fff authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

A brave new world for window insets (9/n)

Implement WindowInsets.get(Max)Insets(int typeMask) to allow the
developer to query the inset by inset type.

Also rework InsetsState.calculateInsets to actually construct the
WindowInsets instance that contains this information.

Test: InsetStateTests
Bug: 118118435
Change-Id: Ie316e074c020bdb9808c11608812dea572c8de5d
parent 297985ab
Loading
Loading
Loading
Loading
+39 −16
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.view;

import static android.view.WindowInsets.Type.indexOf;

import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Insets;
@@ -114,8 +116,8 @@ public class InsetsState implements Parcelable {
    public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
            boolean alwaysConsumeNavBar, DisplayCutout cutout,
            @Nullable @InsetSide SparseIntArray typeSideMap) {
        Insets systemInsets = Insets.NONE;
        Insets maxInsets = Insets.NONE;
        Insets[] typeInsetsMap = new Insets[Type.SIZE];
        Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
        final Rect relativeFrame = new Rect(frame);
        final Rect relativeFrameMax = new Rect(frame);
        for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
@@ -123,32 +125,38 @@ public class InsetsState implements Parcelable {
            if (source == null) {
                continue;
            }
            systemInsets = processSource(source, systemInsets, relativeFrame,
                    false /* ignoreVisibility */, typeSideMap);
            processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap,
                    typeSideMap);

            // IME won't be reported in max insets as the size depends on the EditorInfo of the IME
            // target.
            if (source.getType() != TYPE_IME) {
                maxInsets = processSource(source, maxInsets, relativeFrameMax,
                        true /* ignoreVisibility */, null /* typeSideMap */);
                processSource(source, relativeFrameMax, true /* ignoreVisibility */,
                        typeMaxInsetsMap, null /* typeSideMap */);
            }
        }
        return new WindowInsets(new Rect(systemInsets), new Rect(maxInsets), isScreenRound,
        return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, isScreenRound,
                alwaysConsumeNavBar, cutout);
    }

    private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame,
            boolean ignoreVisibility, @Nullable @InsetSide SparseIntArray typeSideMap) {
        Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility);
        insets = Insets.add(currentInsets, insets);
        relativeFrame.inset(insets);
        if (typeSideMap != null && !Insets.NONE.equals(currentInsets)) {
            @InsetSide int insetSide = getInsetSide(currentInsets);
    private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
            Insets[] typeInsetsMap, @Nullable @InsetSide SparseIntArray typeSideMap) {
        Insets insets = source.calculateInsets(relativeFrame, ignoreVisibility);

        int index = indexOf(toPublicType(source.getType()));
        Insets existing = typeInsetsMap[index];
        if (existing == null) {
            typeInsetsMap[index] = insets;
        } else {
            typeInsetsMap[index] = Insets.max(existing, insets);
        }

        if (typeSideMap != null && !Insets.NONE.equals(insets)) {
            @InsetSide int insetSide = getInsetSide(insets);
            if (insetSide != INSET_SIDE_UNKNWON) {
                typeSideMap.put(source.getType(), getInsetSide(currentInsets));
                typeSideMap.put(source.getType(), getInsetSide(insets));
            }
        }
        return insets;
    }

    /**
@@ -229,6 +237,21 @@ public class InsetsState implements Parcelable {
        return result;
    }

    static @InsetType int toPublicType(@InternalInsetType int type) {
        switch (type) {
            case TYPE_TOP_BAR:
                return Type.TOP_BAR;
            case TYPE_SIDE_BAR_1:
            case TYPE_SIDE_BAR_2:
            case TYPE_SIDE_BAR_3:
                return Type.SIDE_BARS;
            case TYPE_IME:
                return Type.IME;
            default:
                throw new IllegalArgumentException("Unknown type: " + type);
        }
    }

    public static boolean getDefaultVisibly(@InsetType int type) {
        switch (type) {
            case TYPE_TOP_BAR:
+112 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
package android.view;

import static android.view.WindowInsets.Type.FIRST;
import static android.view.WindowInsets.Type.IME;
import static android.view.WindowInsets.Type.LAST;
import static android.view.WindowInsets.Type.SIDE_BARS;
import static android.view.WindowInsets.Type.SIZE;
@@ -33,7 +34,9 @@ import android.annotation.UnsupportedAppUsage;
import android.graphics.Insets;
import android.graphics.Rect;
import android.util.SparseArray;
import android.view.InsetsState.InternalInsetType;
import android.view.WindowInsets.Type.InsetType;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethod;

import com.android.internal.util.Preconditions;
@@ -182,6 +185,18 @@ public final class WindowInsets {
        return result == null ? Insets.NONE : result;
    }

    /**
     * Sets all entries in {@code typeInsetsMap} that belong to {@code typeMask} to {@code insets},
     */
    private static void setInsets(Insets[] typeInsetsMap, @InsetType int typeMask, Insets insets) {
        for (int i = FIRST; i <= LAST; i = i << 1) {
            if ((typeMask & i) == 0) {
                continue;
            }
            typeInsetsMap[indexOf(i)] = insets;
        }
    }

    /** @hide */
    @UnsupportedAppUsage
    public WindowInsets(Rect systemWindowInsets) {
@@ -239,6 +254,45 @@ public final class WindowInsets {
        return getInsets(mTypeInsetsMap, compatSystemInsets());
    }

    /**
     * Returns the insets of a specific set of windows causing insets, denoted by the
     * {@code typeMask} bit mask of {@link InsetType}s.
     *
     * @param typeMask Bit mask of {@link InsetType}s to query the insets for.
     * @return The insets.
     *
     * @hide pending unhide
     */
    public Insets getInsets(@InsetType int typeMask) {
        return getInsets(mTypeInsetsMap, typeMask);
    }

    /**
     * Returns the maximum amount of insets a specific set of windows can cause, denoted by the
     * {@code typeMask} bit mask of {@link InsetType}s.
     *
     * <p>The maximum insets represents the area of a a window that that <b>may</b> be partially
     * or fully obscured by the system window identified by {@code type}. This value does not
     * change based on the visibility state of those elements. for example, if the status bar is
     * normally shown, but temporarily hidden, the maximum inset will still provide the inset
     * associated with the status bar being shown.</p>
     *
     * @param typeMask Bit mask of {@link InsetType}s to query the insets for.
     * @return The insets.
     *
     * @throws IllegalArgumentException If the caller tries to query {@link Type#ime()}. Maximum
     *                                  insets are not available for this type as the height of the
     *                                  IME is dynamic depending on the {@link EditorInfo} of the
     *                                  currently focused view, as well as the UI state of the IME.
     * @hide pending unhide
     */
    public Insets getMaxInsets(@InsetType int typeMask) throws IllegalArgumentException {
        if ((typeMask & IME) != 0) {
            throw new IllegalArgumentException("Unable to query the maximum insets for IME");
        }
        return getInsets(mTypeMaxInsetsMap, typeMask);
    }

    /**
     * Returns the left system window inset in pixels.
     *
@@ -745,6 +799,64 @@ public final class WindowInsets {
            return this;
        }

        /**
         * Sets the insets of a specific window type in pixels.
         *
         * <p>The insets represents the area of a a window that is partially or fully obscured by
         * the system windows identified by {@code typeMask}.
         * </p>
         *
         * @see #getInsets(int)
         *
         * @param typeMask The bitmask of {@link InsetType} to set the insets for.
         * @param insets The insets to set.
         *
         * @return itself
         * @hide pending unhide
         */
        @NonNull
        public Builder setInsets(@InsetType int typeMask, @NonNull Insets insets) {
            Preconditions.checkNotNull(insets);
            WindowInsets.setInsets(mTypeInsetsMap, typeMask, insets);
            mSystemInsetsConsumed = false;
            return this;
        }

        /**
         * Sets the maximum amount of insets a specific window type in pixels.
         *
         * <p>The maximum insets represents the area of a a window that that <b>may</b> be partially
         * or fully obscured by the system windows identified by {@code typeMask}. This value does
         * not change based on the visibility state of those elements. for example, if the status
         * bar is normally shown, but temporarily hidden, the maximum inset will still provide the
         * inset associated with the status bar being shown.</p>
         *
         * @see #getMaxInsets(int)
         *
         * @param typeMask The bitmask of {@link InsetType} to set the insets for.
         * @param insets The insets to set.
         *
         * @return itself
         *
         * @throws IllegalArgumentException If {@code typeMask} contains {@link Type#ime()}. Maximum
         *                                  insets are not available for this type as the height of
         *                                  the IME is dynamic depending on the {@link EditorInfo}
         *                                  of the currently focused view, as well as the UI
         *                                  state of the IME.
         * @hide pending unhide
         */
        @NonNull
        public Builder setMaxInsets(@InsetType int typeMask, @NonNull Insets insets)
                throws IllegalArgumentException{
            if (typeMask == IME) {
                throw new IllegalArgumentException("Maximum inset not available for IME");
            }
            Preconditions.checkNotNull(insets);
            WindowInsets.setInsets(mTypeMaxInsetsMap, typeMask, insets);
            mStableInsetsConsumed = false;
            return this;
        }

        /**
         * Sets the stable insets in pixels.
         *
+14 −3
Original line number Diff line number Diff line
@@ -29,12 +29,14 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNotEquals;

import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.FlakyTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.SparseIntArray;
import android.view.WindowInsets.Type;

import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,9 +58,12 @@ public class InsetsStateTest {
        SparseIntArray typeSideMap = new SparseIntArray();
        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
                DisplayCutout.NO_CUTOUT, typeSideMap);
        assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets());
        assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
        assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
        assertEquals(INSET_SIDE_TOP, typeSideMap.get(TYPE_TOP_BAR));
        assertEquals(INSET_SIDE_BOTTOM, typeSideMap.get(TYPE_IME));
        assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
        assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.ime()));
    }

    @Test
@@ -70,7 +75,11 @@ public class InsetsStateTest {
        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
                DisplayCutout.NO_CUTOUT, null);
        assertEquals(100, insets.getStableInsetBottom());
        assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets());
        assertEquals(Insets.of(0, 0, 0, 100), insets.getMaxInsets(Type.all()));
        assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
        assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.all()));
        assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.sideBars()));
        assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.ime()));
    }

    @Test
@@ -81,7 +90,9 @@ public class InsetsStateTest {
        mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
                DisplayCutout.NO_CUTOUT, null);
        assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets());
        assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
        assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
        assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.sideBars()));
    }

    @Test