diff options
| -rw-r--r-- | api/current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/view/DisplayCutout.java | 24 | ||||
| -rw-r--r-- | core/java/android/view/InsetsSource.java | 4 | ||||
| -rw-r--r-- | core/java/android/view/InsetsState.java | 35 | ||||
| -rw-r--r-- | core/java/android/view/WindowInsets.java | 34 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/DisplayPolicy.java | 24 | ||||
| -rw-r--r-- | services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java | 20 |
7 files changed, 131 insertions, 11 deletions
diff --git a/api/current.txt b/api/current.txt index 8477754d9095..e2d054800d4a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -55520,6 +55520,7 @@ package android.view { public static final class WindowInsets.Type { method public static int captionBar(); + method public static int displayCutout(); method public static int ime(); method public static int mandatorySystemGestures(); method public static int navigationBars(); diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index 0ab856ec5b4d..679c912e846f 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -393,22 +393,38 @@ public final class DisplayCutout { return mSafeInsets.equals(ZERO_RECT); } - /** Returns the inset from the top which avoids the display cutout in pixels. */ + /** + * Returns the inset from the top which avoids the display cutout in pixels. + * + * @see WindowInsets.Type#displayCutout() + */ public int getSafeInsetTop() { return mSafeInsets.top; } - /** Returns the inset from the bottom which avoids the display cutout in pixels. */ + /** + * Returns the inset from the bottom which avoids the display cutout in pixels. + * + * @see WindowInsets.Type#displayCutout() + */ public int getSafeInsetBottom() { return mSafeInsets.bottom; } - /** Returns the inset from the left which avoids the display cutout in pixels. */ + /** + * Returns the inset from the left which avoids the display cutout in pixels. + * + * @see WindowInsets.Type#displayCutout() + */ public int getSafeInsetLeft() { return mSafeInsets.left; } - /** Returns the inset from the right which avoids the display cutout in pixels. */ + /** + * Returns the inset from the right which avoids the display cutout in pixels. + * + * @see WindowInsets.Type#displayCutout() + */ public int getSafeInsetRight() { return mSafeInsets.right; } diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 6caa4fed6409..719f649cb5a2 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -58,6 +58,10 @@ public class InsetsSource implements Parcelable { : null; } + public void setFrame(int left, int top, int right, int bottom) { + mFrame.set(left, top, right, bottom); + } + public void setFrame(Rect frame) { mFrame.set(frame); } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 8648682aaa2e..c877c454be91 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -24,6 +24,7 @@ import static android.view.ViewRootImpl.sNewInsetsMode; import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES; import static android.view.WindowInsets.Type.SIZE; import static android.view.WindowInsets.Type.SYSTEM_GESTURES; +import static android.view.WindowInsets.Type.displayCutout; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.indexOf; import static android.view.WindowInsets.Type.isVisibleInsetsType; @@ -71,6 +72,10 @@ public class InsetsState implements Parcelable { ITYPE_RIGHT_GESTURES, ITYPE_TOP_TAPPABLE_ELEMENT, ITYPE_BOTTOM_TAPPABLE_ELEMENT, + ITYPE_LEFT_DISPLAY_CUTOUT, + ITYPE_TOP_DISPLAY_CUTOUT, + ITYPE_RIGHT_DISPLAY_CUTOUT, + ITYPE_BOTTOM_DISPLAY_CUTOUT, ITYPE_IME }) public @interface InternalInsetsType {} @@ -88,8 +93,13 @@ public class InsetsState implements Parcelable { public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 7; public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 8; + public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 9; + public static final int ITYPE_TOP_DISPLAY_CUTOUT = 10; + public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 11; + public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 12; + /** Input method window. */ - public static final int ITYPE_IME = 9; + public static final int ITYPE_IME = 13; static final int LAST_TYPE = ITYPE_IME; @@ -185,8 +195,8 @@ public class InsetsState implements Parcelable { final int softInputAdjustMode = legacySoftInputMode & SOFT_INPUT_MASK_ADJUST; return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound, alwaysConsumeSystemBars, cutout, softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE - ? systemBars() | ime() - : systemBars(), + ? systemBars() | displayCutout() | ime() + : systemBars() | displayCutout(), sNewInsetsMode == NEW_INSETS_MODE_FULL && (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0); } @@ -358,6 +368,12 @@ public class InsetsState implements Parcelable { if ((types & Type.CAPTION_BAR) != 0) { result.add(ITYPE_CAPTION_BAR); } + if ((types & Type.DISPLAY_CUTOUT) != 0) { + result.add(ITYPE_LEFT_DISPLAY_CUTOUT); + result.add(ITYPE_TOP_DISPLAY_CUTOUT); + result.add(ITYPE_RIGHT_DISPLAY_CUTOUT); + result.add(ITYPE_BOTTOM_DISPLAY_CUTOUT); + } if ((types & Type.IME) != 0) { result.add(ITYPE_IME); } @@ -388,6 +404,11 @@ public class InsetsState implements Parcelable { case ITYPE_TOP_TAPPABLE_ELEMENT: case ITYPE_BOTTOM_TAPPABLE_ELEMENT: return Type.TAPPABLE_ELEMENT; + case ITYPE_LEFT_DISPLAY_CUTOUT: + case ITYPE_TOP_DISPLAY_CUTOUT: + case ITYPE_RIGHT_DISPLAY_CUTOUT: + case ITYPE_BOTTOM_DISPLAY_CUTOUT: + return Type.DISPLAY_CUTOUT; default: throw new IllegalArgumentException("Unknown type: " + type); } @@ -437,6 +458,14 @@ public class InsetsState implements Parcelable { return "ITYPE_TOP_TAPPABLE_ELEMENT"; case ITYPE_BOTTOM_TAPPABLE_ELEMENT: return "ITYPE_BOTTOM_TAPPABLE_ELEMENT"; + case ITYPE_LEFT_DISPLAY_CUTOUT: + return "ITYPE_LEFT_DISPLAY_CUTOUT"; + case ITYPE_TOP_DISPLAY_CUTOUT: + return "ITYPE_TOP_DISPLAY_CUTOUT"; + case ITYPE_RIGHT_DISPLAY_CUTOUT: + return "ITYPE_RIGHT_DISPLAY_CUTOUT"; + case ITYPE_BOTTOM_DISPLAY_CUTOUT: + return "ITYPE_BOTTOM_DISPLAY_CUTOUT"; case ITYPE_IME: return "ITYPE_IME"; default: diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index a6c311e1daa5..20fa5cab0464 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -17,6 +17,7 @@ package android.view; +import static android.view.WindowInsets.Type.DISPLAY_CUTOUT; import static android.view.WindowInsets.Type.FIRST; import static android.view.WindowInsets.Type.IME; import static android.view.WindowInsets.Type.LAST; @@ -1209,6 +1210,13 @@ public final class WindowInsets { @NonNull public Builder setDisplayCutout(@Nullable DisplayCutout displayCutout) { mDisplayCutout = displayCutout != null ? displayCutout : DisplayCutout.NO_CUTOUT; + if (!mDisplayCutout.isEmpty()) { + final Insets safeInsets = Insets.of(mDisplayCutout.getSafeInsets()); + final int index = indexOf(DISPLAY_CUTOUT); + mTypeInsetsMap[index] = safeInsets; + mTypeMaxInsetsMap[index] = safeInsets; + mTypeVisibilityMap[index] = true; + } return this; } @@ -1256,8 +1264,10 @@ public final class WindowInsets { static final int MANDATORY_SYSTEM_GESTURES = 1 << 5; static final int TAPPABLE_ELEMENT = 1 << 6; - static final int LAST = 1 << 7; - static final int SIZE = 8; + static final int DISPLAY_CUTOUT = 1 << 7; + + static final int LAST = 1 << 8; + static final int SIZE = 9; static final int WINDOW_DECOR = LAST; static int indexOf(@InsetsType int type) { @@ -1276,8 +1286,10 @@ public final class WindowInsets { return 5; case TAPPABLE_ELEMENT: return 6; - case WINDOW_DECOR: + case DISPLAY_CUTOUT: return 7; + case WINDOW_DECOR: + return 8; default: throw new IllegalArgumentException("type needs to be >= FIRST and <= LAST," + " type=" + type); @@ -1290,7 +1302,7 @@ public final class WindowInsets { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, value = {STATUS_BARS, NAVIGATION_BARS, CAPTION_BAR, IME, WINDOW_DECOR, - SYSTEM_GESTURES, MANDATORY_SYSTEM_GESTURES, TAPPABLE_ELEMENT}) + SYSTEM_GESTURES, MANDATORY_SYSTEM_GESTURES, TAPPABLE_ELEMENT, DISPLAY_CUTOUT}) public @interface InsetsType { } @@ -1358,6 +1370,20 @@ public final class WindowInsets { } /** + * Returns an insets type representing the area that used by {@link DisplayCutout}. + * + * <p>This is equivalent to the safe insets on {@link #getDisplayCutout()}.</p> + * + * @see DisplayCutout#getSafeInsetLeft() + * @see DisplayCutout#getSafeInsetTop() + * @see DisplayCutout#getSafeInsetRight() + * @see DisplayCutout#getSafeInsetBottom() + */ + public static @InsetsType int displayCutout() { + return DISPLAY_CUTOUT; + } + + /** * @return All system bars. Includes {@link #statusBars()}, {@link #captionBar()} as well as * {@link #navigationBars()}, but not {@link #ime()}. */ diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 4e872ac9507e..44507390142c 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -25,12 +25,16 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND import static android.content.res.Configuration.UI_MODE_TYPE_CAR; import static android.content.res.Configuration.UI_MODE_TYPE_MASK; import static android.view.Display.TYPE_INTERNAL; +import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; +import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_LEFT_GESTURES; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; +import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_RIGHT_GESTURES; import static android.view.InsetsState.ITYPE_STATUS_BAR; +import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_TOP_GESTURES; import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; @@ -1430,6 +1434,7 @@ public class DisplayPolicy { */ void simulateLayoutDisplay(DisplayFrames displayFrames, InsetsState insetsState, int uiMode) { displayFrames.onBeginLayout(); + updateInsetsStateForDisplayCutout(displayFrames, insetsState); insetsState.setDisplayFrame(displayFrames.mUnrestricted); final WindowFrames simulatedWindowFrames = new WindowFrames(); if (mNavigationBar != null) { @@ -1458,6 +1463,8 @@ public class DisplayPolicy { */ public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { displayFrames.onBeginLayout(); + updateInsetsStateForDisplayCutout(displayFrames, + mDisplayContent.getInsetsStateController().getRawInsetsState()); mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); @@ -1524,6 +1531,23 @@ public class DisplayPolicy { mLastNotificationShadeForcesShowingNavigation = notificationShadeForcesShowingNavigation; } + private static void updateInsetsStateForDisplayCutout(DisplayFrames displayFrames, + InsetsState state) { + if (displayFrames.mDisplayCutout.getDisplayCutout().isEmpty()) { + state.removeSource(ITYPE_LEFT_DISPLAY_CUTOUT); + state.removeSource(ITYPE_TOP_DISPLAY_CUTOUT); + state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT); + state.removeSource(ITYPE_BOTTOM_DISPLAY_CUTOUT); + return; + } + final Rect u = displayFrames.mUnrestricted; + final Rect s = displayFrames.mDisplayCutoutSafe; + state.getSource(ITYPE_LEFT_DISPLAY_CUTOUT).setFrame(u.left, u.top, s.left, u.bottom); + state.getSource(ITYPE_TOP_DISPLAY_CUTOUT).setFrame(u.left, u.top, u.right, s.top); + state.getSource(ITYPE_RIGHT_DISPLAY_CUTOUT).setFrame(s.right, u.top, u.right, u.bottom); + state.getSource(ITYPE_BOTTOM_DISPLAY_CUTOUT).setFrame(u.left, s.bottom, u.right, u.bottom); + } + /** Enforces the last layout policy for display frames. */ private void postAdjustDisplayFrames(DisplayFrames displayFrames) { if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 203fa61b7546..e2c27ea55b13 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -269,6 +269,26 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); } + @Test + public void layoutWindowLw_fitDisplayCutout() { + assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL); + addDisplayCutout(); + + mWindow.mAttrs.setFitInsetsTypes(Type.displayCutout()); + mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0); + assertInsetByTopBottom(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0); + assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + } + // TODO(b/118118435): remove after migration @Test public void layoutWindowLw_appDrawsBarsLegacy() { |