diff options
11 files changed, 225 insertions, 51 deletions
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 5c10db19b403..b796e0b1c429 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -104,12 +104,23 @@ public class InsetsSource implements Parcelable { */ public static final int FLAG_ANIMATE_RESIZING = 1 << 3; + /** + * Controls whether the {@link WindowInsets.Type#captionBar()} insets provided by this source + * should always be forcibly consumed. Unlike with {@link #FLAG_FORCE_CONSUMING}, when this + * flag is used the caption bar will be consumed even when the bar is requested to be visible. + * + * Note: this flag does not take effect when the window applies + * {@link WindowInsetsController.Appearance#APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND}. + */ + public static final int FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR = 1 << 4; + @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = "FLAG_", value = { FLAG_SUPPRESS_SCRIM, FLAG_INSETS_ROUNDED_CORNER, FLAG_FORCE_CONSUMING, FLAG_ANIMATE_RESIZING, + FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR }) public @interface Flags {} @@ -555,6 +566,9 @@ public class InsetsSource implements Parcelable { if ((flags & FLAG_ANIMATE_RESIZING) != 0) { joiner.add("ANIMATE_RESIZING"); } + if ((flags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0) { + joiner.add("FORCE_CONSUMING_OPAQUE_CAPTION_BAR"); + } return joiner.toString(); } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index bbd9acfd4cd7..6b4340a02edc 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -19,6 +19,7 @@ package android.view; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.util.SequenceUtils.getInitSeq; import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; +import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER; import static android.view.InsetsStateProto.DISPLAY_CUTOUT; import static android.view.InsetsStateProto.DISPLAY_FRAME; @@ -54,6 +55,7 @@ import android.view.WindowInsets.Type.InsetsType; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags; import java.io.PrintWriter; import java.util.Objects; @@ -131,18 +133,25 @@ public class InsetsState implements Parcelable { final Rect relativeFrame = new Rect(frame); final Rect relativeFrameMax = new Rect(frame); @InsetsType int forceConsumingTypes = 0; + boolean forceConsumingOpaqueCaptionBar = false; @InsetsType int suppressScrimTypes = 0; final Rect[][] typeBoundingRectsMap = new Rect[Type.SIZE][]; final Rect[][] typeMaxBoundingRectsMap = new Rect[Type.SIZE][]; for (int i = mSources.size() - 1; i >= 0; i--) { final InsetsSource source = mSources.valueAt(i); final @InsetsType int type = source.getType(); + final @InsetsSource.Flags int flags = source.getFlags(); - if ((source.getFlags() & InsetsSource.FLAG_FORCE_CONSUMING) != 0) { + if ((flags & InsetsSource.FLAG_FORCE_CONSUMING) != 0) { forceConsumingTypes |= type; } - if ((source.getFlags() & InsetsSource.FLAG_SUPPRESS_SCRIM) != 0) { + if (Flags.enableCaptionCompatInsetForceConsumptionAlways() + && (flags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0) { + forceConsumingOpaqueCaptionBar = true; + } + + if ((flags & InsetsSource.FLAG_SUPPRESS_SCRIM) != 0) { suppressScrimTypes |= type; } @@ -177,7 +186,8 @@ public class InsetsState implements Parcelable { } return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound, - forceConsumingTypes, suppressScrimTypes, calculateRelativeCutout(frame), + forceConsumingTypes, forceConsumingOpaqueCaptionBar, suppressScrimTypes, + calculateRelativeCutout(frame), calculateRelativeRoundedCorners(frame), calculateRelativePrivacyIndicatorBounds(frame), calculateRelativeDisplayShape(frame), diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 561d979f2f9d..297e8ffa0bca 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -97,6 +97,7 @@ public final class WindowInsets { private final int mFrameHeight; private final @InsetsType int mForceConsumingTypes; + private final boolean mForceConsumingOpaqueCaptionBar; private final @InsetsType int mSuppressScrimTypes; private final boolean mSystemWindowInsetsConsumed; private final boolean mStableInsetsConsumed; @@ -123,7 +124,7 @@ public final class WindowInsets { static { CONSUMED = new WindowInsets(createCompatTypeMap(null), createCompatTypeMap(null), - createCompatVisibilityMap(createCompatTypeMap(null)), false, 0, 0, null, + createCompatVisibilityMap(createCompatTypeMap(null)), false, 0, false, 0, null, null, null, null, systemBars(), false, null, null, 0, 0); } @@ -144,6 +145,7 @@ public final class WindowInsets { boolean[] typeVisibilityMap, boolean isRound, @InsetsType int forceConsumingTypes, + boolean forceConsumingOpaqueCaptionBar, @InsetsType int suppressScrimTypes, DisplayCutout displayCutout, RoundedCorners roundedCorners, @@ -166,6 +168,7 @@ public final class WindowInsets { mTypeVisibilityMap = typeVisibilityMap; mIsRound = isRound; mForceConsumingTypes = forceConsumingTypes; + mForceConsumingOpaqueCaptionBar = forceConsumingOpaqueCaptionBar; mSuppressScrimTypes = suppressScrimTypes; mCompatInsetsTypes = compatInsetsTypes; mCompatIgnoreVisibility = compatIgnoreVisibility; @@ -196,7 +199,9 @@ public final class WindowInsets { this(src.mSystemWindowInsetsConsumed ? null : src.mTypeInsetsMap, src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap, src.mTypeVisibilityMap, src.mIsRound, - src.mForceConsumingTypes, src.mSuppressScrimTypes, + src.mForceConsumingTypes, + src.mForceConsumingOpaqueCaptionBar, + src.mSuppressScrimTypes, displayCutoutCopyConstructorArgument(src), src.mRoundedCorners, src.mPrivacyIndicatorBounds, @@ -257,7 +262,7 @@ public final class WindowInsets { /** @hide */ @UnsupportedAppUsage public WindowInsets(Rect systemWindowInsets) { - this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, 0, 0, + this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, 0, false, 0, null, null, null, null, systemBars(), false /* compatIgnoreVisibility */, new Rect[SIZE][], null, 0, 0); } @@ -675,10 +680,10 @@ public final class WindowInsets { return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap, mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap, - mIsRound, mForceConsumingTypes, mSuppressScrimTypes, - null /* displayCutout */, mRoundedCorners, mPrivacyIndicatorBounds, mDisplayShape, - mCompatInsetsTypes, mCompatIgnoreVisibility, - mSystemWindowInsetsConsumed ? null : mTypeBoundingRectsMap, + mIsRound, mForceConsumingTypes, mForceConsumingOpaqueCaptionBar, + mSuppressScrimTypes, null /* displayCutout */, mRoundedCorners, + mPrivacyIndicatorBounds, mDisplayShape, mCompatInsetsTypes, + mCompatIgnoreVisibility, mSystemWindowInsetsConsumed ? null : mTypeBoundingRectsMap, mStableInsetsConsumed ? null : mTypeMaxBoundingRectsMap, mFrameWidth, mFrameHeight); } @@ -729,7 +734,8 @@ public final class WindowInsets { public WindowInsets consumeSystemWindowInsets() { return new WindowInsets(null, null, mTypeVisibilityMap, - mIsRound, mForceConsumingTypes, mSuppressScrimTypes, + mIsRound, mForceConsumingTypes, mForceConsumingOpaqueCaptionBar, + mSuppressScrimTypes, // If the system window insets types contain displayCutout, we should also consume // it. (mCompatInsetsTypes & displayCutout()) != 0 @@ -1024,6 +1030,13 @@ public final class WindowInsets { /** * @hide */ + public boolean isForceConsumingOpaqueCaptionBar() { + return mForceConsumingOpaqueCaptionBar; + } + + /** + * @hide + */ public @InsetsType int getSuppressScrimTypes() { return mSuppressScrimTypes; } @@ -1058,6 +1071,8 @@ public final class WindowInsets { result.append("\n "); result.append("forceConsumingTypes=" + Type.toString(mForceConsumingTypes)); result.append("\n "); + result.append("forceConsumingOpaqueCaptionBar=" + mForceConsumingOpaqueCaptionBar); + result.append("\n "); result.append("suppressScrimTypes=" + Type.toString(mSuppressScrimTypes)); result.append("\n "); result.append("compatInsetsTypes=" + Type.toString(mCompatInsetsTypes)); @@ -1180,7 +1195,8 @@ public final class WindowInsets { ? null : insetInsets(mTypeMaxInsetsMap, left, top, right, bottom), mTypeVisibilityMap, - mIsRound, mForceConsumingTypes, mSuppressScrimTypes, + mIsRound, mForceConsumingTypes, mForceConsumingOpaqueCaptionBar, + mSuppressScrimTypes, mDisplayCutoutConsumed ? null : mDisplayCutout == null @@ -1214,6 +1230,7 @@ public final class WindowInsets { return mIsRound == that.mIsRound && mForceConsumingTypes == that.mForceConsumingTypes + && mForceConsumingOpaqueCaptionBar == that.mForceConsumingOpaqueCaptionBar && mSuppressScrimTypes == that.mSuppressScrimTypes && mSystemWindowInsetsConsumed == that.mSystemWindowInsetsConsumed && mStableInsetsConsumed == that.mStableInsetsConsumed @@ -1235,9 +1252,9 @@ public final class WindowInsets { public int hashCode() { return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap), Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout, mRoundedCorners, - mForceConsumingTypes, mSuppressScrimTypes, mSystemWindowInsetsConsumed, - mStableInsetsConsumed, mDisplayCutoutConsumed, mPrivacyIndicatorBounds, - mDisplayShape, Arrays.deepHashCode(mTypeBoundingRectsMap), + mForceConsumingTypes, mForceConsumingOpaqueCaptionBar, mSuppressScrimTypes, + mSystemWindowInsetsConsumed, mStableInsetsConsumed, mDisplayCutoutConsumed, + mPrivacyIndicatorBounds, mDisplayShape, Arrays.deepHashCode(mTypeBoundingRectsMap), Arrays.deepHashCode(mTypeMaxBoundingRectsMap), mFrameWidth, mFrameHeight); } @@ -1367,6 +1384,7 @@ public final class WindowInsets { private boolean mIsRound; private @InsetsType int mForceConsumingTypes; + private boolean mForceConsumingOpaqueCaptionBar; private @InsetsType int mSuppressScrimTypes; private PrivacyIndicatorBounds mPrivacyIndicatorBounds = new PrivacyIndicatorBounds(); @@ -1399,6 +1417,7 @@ public final class WindowInsets { mRoundedCorners = insets.mRoundedCorners; mIsRound = insets.mIsRound; mForceConsumingTypes = insets.mForceConsumingTypes; + mForceConsumingOpaqueCaptionBar = insets.mForceConsumingOpaqueCaptionBar; mSuppressScrimTypes = insets.mSuppressScrimTypes; mPrivacyIndicatorBounds = insets.mPrivacyIndicatorBounds; mDisplayShape = insets.mDisplayShape; @@ -1687,6 +1706,13 @@ public final class WindowInsets { /** @hide */ @NonNull + public Builder setForceConsumingOpaqueCaptionBar(boolean forceConsumingOpaqueCaptionBar) { + mForceConsumingOpaqueCaptionBar = forceConsumingOpaqueCaptionBar; + return this; + } + + /** @hide */ + @NonNull public Builder setSuppressScrimTypes(@InsetsType int suppressScrimTypes) { mSuppressScrimTypes = suppressScrimTypes; return this; @@ -1763,9 +1789,9 @@ public final class WindowInsets { public WindowInsets build() { return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap, mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap, - mIsRound, mForceConsumingTypes, mSuppressScrimTypes, mDisplayCutout, - mRoundedCorners, mPrivacyIndicatorBounds, mDisplayShape, systemBars(), - false /* compatIgnoreVisibility */, + mIsRound, mForceConsumingTypes, mForceConsumingOpaqueCaptionBar, + mSuppressScrimTypes, mDisplayCutout, mRoundedCorners, mPrivacyIndicatorBounds, + mDisplayShape, systemBars(), false /* compatIgnoreVisibility */, mSystemInsetsConsumed ? null : mTypeBoundingRectsMap, mStableInsetsConsumed ? null : mTypeMaxBoundingRectsMap, mFrameWidth, mFrameHeight); diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 87e22ed42cbd..48283930595d 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -29,6 +29,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowInsetsController.APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; +import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; @@ -36,6 +37,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.flags.Flags.customizableWindowHeaders; import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL; @@ -226,6 +228,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private boolean mLastHasLeftStableInset = false; private int mLastWindowFlags = 0; private @InsetsType int mLastForceConsumingTypes = 0; + private boolean mLastForceConsumingOpaqueCaptionBar = false; private @InsetsType int mLastSuppressScrimTypes = 0; private int mRootScrollY = 0; @@ -1068,8 +1071,12 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind WindowManager.LayoutParams attrs = mWindow.getAttributes(); int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility(); + final ViewRootImpl viewRoot = getViewRootImpl(); final WindowInsetsController controller = getWindowInsetsController(); final @InsetsType int requestedVisibleTypes = controller.getRequestedVisibleTypes(); + final @Appearance int appearance = viewRoot != null + ? viewRoot.mWindowAttributes.insetsFlags.appearance + : controller.getSystemBarsAppearance(); // IME is an exceptional floating window that requires color view. final boolean isImeWindow = @@ -1080,13 +1087,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; mLastWindowFlags = attrs.flags; - final ViewRootImpl viewRoot = getViewRootImpl(); - final @Appearance int appearance = viewRoot != null - ? viewRoot.mWindowAttributes.insetsFlags.appearance - : controller.getSystemBarsAppearance(); - if (insets != null) { mLastForceConsumingTypes = insets.getForceConsumingTypes(); + mLastForceConsumingOpaqueCaptionBar = insets.isForceConsumingOpaqueCaptionBar(); final boolean clearsCompatInsets = clearsCompatInsets(attrs.type, attrs.flags, getResources().getConfiguration().windowConfiguration.getActivityType(), @@ -1209,16 +1212,20 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind final boolean hideCaptionBar = fullscreen || (requestedVisibleTypes & WindowInsets.Type.captionBar()) == 0; - final boolean consumingCaptionBar = - ((mLastForceConsumingTypes & WindowInsets.Type.captionBar()) != 0 + final boolean consumingCaptionBar = Flags.enableCaptionCompatInsetForceConsumption() + && ((mLastForceConsumingTypes & WindowInsets.Type.captionBar()) != 0 && hideCaptionBar); - final int consumedTop; - if (Flags.enableCaptionCompatInsetForceConsumption()) { - consumedTop = (consumingStatusBar || consumingCaptionBar) ? mLastTopInset : 0; - } else { - consumedTop = consumingStatusBar ? mLastTopInset : 0; - } + final boolean isOpaqueCaptionBar = customizableWindowHeaders() + && (appearance & APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) == 0; + final boolean consumingOpaqueCaptionBar = + Flags.enableCaptionCompatInsetForceConsumptionAlways() + && mLastForceConsumingOpaqueCaptionBar + && isOpaqueCaptionBar; + + final int consumedTop = + (consumingStatusBar || consumingCaptionBar || consumingOpaqueCaptionBar) + ? mLastTopInset : 0; int consumedRight = consumingNavBar ? mLastRightInset : 0; int consumedBottom = consumingNavBar ? mLastBottomInset : 0; int consumedLeft = consumingNavBar ? mLastLeftInset : 0; diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index 16bd20a42b2a..75749c7eff09 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -19,6 +19,7 @@ package android.view; 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.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; import static android.view.InsetsSource.ID_IME; import static android.view.InsetsSource.SIDE_BOTTOM; import static android.view.InsetsSource.SIDE_TOP; @@ -54,12 +55,17 @@ import static org.junit.Assert.assertTrue; import android.graphics.Insets; import android.graphics.Rect; import android.os.Parcel; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.SparseIntArray; import android.view.WindowInsets.Type; import androidx.test.runner.AndroidJUnit4; +import com.android.window.flags.Flags; + +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -78,6 +84,9 @@ import java.util.List; @RunWith(AndroidJUnit4.class) public class InsetsStateTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final int ID_STATUS_BAR = InsetsSource.createId( null /* owner */, 0 /* index */, statusBars()); private static final int ID_NAVIGATION_BAR = InsetsSource.createId( @@ -854,4 +863,19 @@ public class InsetsStateTest { ); } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS) + public void testCalculateInsets_forceConsumingCaptionBar() { + mState.getOrCreateSource(ID_CAPTION_BAR, captionBar()) + .setFrame(new Rect(0, 0, 100, 100)) + .setVisible(true) + .setFlags(FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR); + + final WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 1000, 1000), null, false, + SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, + new SparseIntArray()); + + assertTrue(insets.isForceConsumingOpaqueCaptionBar()); + } } diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java index ab4543cb8001..00b3b09d1cc9 100644 --- a/core/tests/coretests/src/android/view/WindowInsetsTest.java +++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java @@ -40,14 +40,14 @@ public class WindowInsetsTest { @Test public void systemWindowInsets_afterConsuming_isConsumed() { assertTrue(new WindowInsets(WindowInsets.createCompatTypeMap(new Rect(1, 2, 3, 4)), null, - null, false, 0, 0, null, null, null, null, + null, false, 0, false, 0, null, null, null, null, WindowInsets.Type.systemBars(), false, null, null, 0, 0) .consumeSystemWindowInsets().isConsumed()); } @Test public void multiNullConstructor_isConsumed() { - assertTrue(new WindowInsets(null, null, null, false, 0, 0, null, null, null, null, + assertTrue(new WindowInsets(null, null, null, false, 0, false, 0, null, null, null, null, WindowInsets.Type.systemBars(), false, null, null, 0, 0).isConsumed()); } @@ -64,7 +64,7 @@ public class WindowInsetsTest { WindowInsets.assignCompatInsets(maxInsets, new Rect(0, 10, 0, 0)); WindowInsets.assignCompatInsets(insets, new Rect(0, 0, 0, 0)); WindowInsets windowInsets = new WindowInsets(insets, maxInsets, visible, false, 0, - 0, null, null, null, DisplayShape.NONE, systemBars(), + false, 0, null, null, null, DisplayShape.NONE, systemBars(), true /* compatIgnoreVisibility */, null, null, 0, 0); assertEquals(Insets.of(0, 10, 0, 0), windowInsets.getSystemWindowInsets()); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 529def7ca3d7..2ebb9fe46c64 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -19,6 +19,8 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.windowingModeToString; +import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; +import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; @@ -574,6 +576,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // through to the windows below so that the app can respond to input events on // their custom content. relayoutParams.mInputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY; + } else { + if (Flags.enableCaptionCompatInsetForceConsumption()) { + // Force-consume the caption bar insets when the app tries to hide the caption. + // This improves app compatibility of immersive apps. + relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING; + } + } + if (Flags.enableCaptionCompatInsetForceConsumptionAlways()) { + // Always force-consume the caption bar insets for maximum app compatibility, + // including non-immersive apps that just don't handle caption insets properly. + relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; } // Report occluding elements as bounding rects to the insets system so that apps can // draw in the empty space in the center: diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index a691f59a2155..5f00aa28d666 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -19,7 +19,6 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED; -import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowInsets.Type.mandatorySystemGestures; import static android.view.WindowInsets.Type.statusBars; @@ -54,7 +53,6 @@ import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; -import com.android.window.flags.Flags; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; @@ -375,7 +373,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } final WindowDecorationInsets newInsets = new WindowDecorationInsets( - mTaskInfo.token, mOwner, captionInsetsRect, boundingRects); + mTaskInfo.token, mOwner, captionInsetsRect, boundingRects, + params.mInsetSourceFlags); if (!newInsets.equals(mWindowDecorationInsets)) { // Add or update this caption as an insets source. mWindowDecorationInsets = newInsets; @@ -660,7 +659,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final int captionHeight = loadDimensionPixelSize(mContext.getResources(), captionHeightId); final Rect captionInsets = new Rect(0, 0, 0, captionHeight); final WindowDecorationInsets newInsets = new WindowDecorationInsets(mTaskInfo.token, - mOwner, captionInsets, null /* boundingRets */); + mOwner, captionInsets, null /* boundingRets */, 0 /* flags */); if (!newInsets.equals(mWindowDecorationInsets)) { mWindowDecorationInsets = newInsets; mWindowDecorationInsets.addOrUpdate(wct); @@ -674,6 +673,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> int mCaptionWidthId; final List<OccludingCaptionElement> mOccludingCaptionElements = new ArrayList<>(); int mInputFeatures; + @InsetsSource.Flags int mInsetSourceFlags; int mShadowRadiusId; int mCornerRadius; @@ -689,6 +689,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mCaptionWidthId = Resources.ID_NULL; mOccludingCaptionElements.clear(); mInputFeatures = 0; + mInsetSourceFlags = 0; mShadowRadiusId = Resources.ID_NULL; mCornerRadius = 0; @@ -753,20 +754,20 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> private final Binder mOwner; private final Rect mFrame; private final Rect[] mBoundingRects; + private final @InsetsSource.Flags int mFlags; private WindowDecorationInsets(WindowContainerToken token, Binder owner, Rect frame, - Rect[] boundingRects) { + Rect[] boundingRects, @InsetsSource.Flags int flags) { mToken = token; mOwner = owner; mFrame = frame; mBoundingRects = boundingRects; + mFlags = flags; } void addOrUpdate(WindowContainerTransaction wct) { - final @InsetsSource.Flags int captionSourceFlags = - Flags.enableCaptionCompatInsetForceConsumption() ? FLAG_FORCE_CONSUMING : 0; wct.addInsetsSource(mToken, mOwner, INDEX, captionBar(), mFrame, mBoundingRects, - captionSourceFlags); + mFlags); wct.addInsetsSource(mToken, mOwner, INDEX, mandatorySystemGestures(), mFrame, mBoundingRects, 0 /* flags */); } @@ -782,12 +783,13 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> if (!(o instanceof WindowDecoration.WindowDecorationInsets that)) return false; return Objects.equals(mToken, that.mToken) && Objects.equals(mOwner, that.mOwner) && Objects.equals(mFrame, that.mFrame) - && Objects.deepEquals(mBoundingRects, that.mBoundingRects); + && Objects.deepEquals(mBoundingRects, that.mBoundingRects) + && mFlags == that.mFlags; } @Override public int hashCode() { - return Objects.hash(mToken, mOwner, mFrame, Arrays.hashCode(mBoundingRects)); + return Objects.hash(mToken, mOwner, mFrame, Arrays.hashCode(mBoundingRects), mFlags); } } -} +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java index 412fef30d4fb..81434919b2d8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java @@ -20,6 +20,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; +import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; +import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; @@ -398,6 +400,80 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { } @Test + @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION) + public void updateRelayoutParams_defaultHeader_addsForceConsumingFlag() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance(0); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); + + assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION) + public void updateRelayoutParams_customHeader_noForceConsumptionFlag() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance( + APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); + + assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS) + public void updateRelayoutParams_header_addsForceConsumingCaptionBar() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); + + assertThat( + (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0) + .isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS) + public void updateRelayoutParams_handle_skipsForceConsumingCaptionBar() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); + + assertThat( + (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) == 0) + .isTrue(); + } + @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR) public void relayout_fullscreenTask_appliesTransactionImmediately() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index 2d1bf14ffbb3..ca6e03c45e7e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; +import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowInsets.Type.mandatorySystemGestures; import static android.view.WindowInsets.Type.statusBars; @@ -56,7 +57,6 @@ import android.content.res.Resources; import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; -import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.util.DisplayMetrics; @@ -75,7 +75,6 @@ import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; import com.android.dx.mockito.inline.extended.StaticMockitoSession; -import com.android.window.flags.Flags; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; @@ -781,8 +780,7 @@ public class WindowDecorationTests extends ShellTestCase { } @Test - @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION) - public void testRelayout_captionInsetForceConsume() { + public void testRelayout_captionInsetSourceFlags() { final Display defaultDisplay = mock(Display.class); doReturn(defaultDisplay).when(mMockDisplayController) .getDisplay(Display.DEFAULT_DISPLAY); @@ -794,11 +792,14 @@ public class WindowDecorationTests extends ShellTestCase { final ActivityManager.RunningTaskInfo taskInfo = builder.setToken(token).setBounds(new Rect(0, 0, 1000, 1000)).build(); final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo); + mRelayoutParams.mInsetSourceFlags = + FLAG_FORCE_CONSUMING | FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; windowDecor.relayout(taskInfo); - // Caption inset source should be force-consuming. + // Caption inset source should add params' flags. verify(mMockWindowContainerTransaction).addInsetsSource(eq(token), any(), - eq(0) /* index */, eq(captionBar()), any(), any(), eq(FLAG_FORCE_CONSUMING)); + eq(0) /* index */, eq(captionBar()), any(), any(), + eq(FLAG_FORCE_CONSUMING | FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt index fd2dead02c6c..e670884eff17 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt @@ -334,6 +334,7 @@ class PhoneStatusBarViewTest : SysuiTestCase() { /* typeVisibilityMap = */ booleanArrayOf(), /* isRound = */ false, /* forceConsumingTypes = */ 0, + /* forceConsumingCaptionBar = */ false, /* suppressScrimTypes = */ 0, /* displayCutout = */ DisplayCutout.NO_CUTOUT, /* roundedCorners = */ RoundedCorners.NO_ROUNDED_CORNERS, |