diff options
| author | 2024-07-11 21:28:32 +0000 | |
|---|---|---|
| committer | 2024-07-23 21:15:11 +0000 | |
| commit | 507dca83b33a1afe23331b5d3095f1efb5d7520f (patch) | |
| tree | 4e8869d21634299e685af37dd59f158b1fbf87a0 | |
| parent | 36a3e6e7f5ebe845847c320e11e35a8d2fd0542f (diff) | |
Add FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR
Adds the flag to the InsetsSource created by the caption bar in shell,
and makes the DecorView consume the caption bar insets when set,
regardless of whether the caption bar is hidden or shown, as long as the
window is requesting opaque caption bars (i.e. did not set
APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND)
Bug: 352563889
Flag: com.android.window.flags.enable_caption_compat_inset_force_consumption_always
Test: open non-immersive apps in freeform that don't handle caption bar
insets (Naver App, Spotify or OneDrive), check caption doesn't overlap
with app content
Test: atest DesktopModeWindowDecorationTests WindowDecorationTests
WindowInsetsTest InsetStateTest
Change-Id: Idb16f647f6a77e04b634e700b5345335b24d284d
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, |