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

Commit 8ce9cb3d authored by Yunfan Chen's avatar Yunfan Chen
Browse files

Support display cutout side override

With this change, the device manufacturers can define the preferred
side of the display cutouts, and the preferred side will be used to
override other decision making logic.

It provide the flexibility to the display cutout policy.

Bug: 302387383
Test: DisplayCutoutTest
Change-Id: I0bc72366cdf75c36f4e5c11dd41b2bfee4cea6a4
parent 02ecb0b4
Loading
Loading
Loading
Loading
+256 −58

File changed.

Preview size limit exceeded, changes collapsed.

+1 −0
Original line number Diff line number Diff line
@@ -32,4 +32,5 @@ message DisplayCutoutProto {
    optional .android.graphics.RectProto bound_right = 5;
    optional .android.graphics.RectProto bound_bottom = 6;
    optional .android.graphics.RectProto waterfall_insets = 7;
    repeated int32 side_overrides = 8;
}
+19 −0
Original line number Diff line number Diff line
@@ -4014,6 +4014,18 @@
         by shrinking the display such that it does not overlap the cutout area. -->
    <bool name="config_maskMainBuiltInDisplayCutout">false</bool>

    <!-- This string array provide override side of each rotation of the given insets.
         Array of "[rotation],[side]".
         Undefined rotation will apply the default behavior.
         When there are cutouts on multiple edges of the display, the override won't take any
         effect. -->
    <string-array name="config_mainBuiltInDisplayCutoutSideOverride" translatable="false">
        <!-- Example:
        <item>90,top</item>
        <item>270,bottom</item>
        -->
    </string-array>

    <!-- Ultrasound support for Mic/speaker path -->
    <!-- Whether the default microphone audio source supports near-ultrasound frequencies
         (range of 18 - 21 kHz). -->
@@ -6370,6 +6382,8 @@
    </string>
    <bool name="config_fillSecondaryBuiltInDisplayCutout">false</bool>
    <bool name="config_maskSecondaryBuiltInDisplayCutout">false</bool>
    <string-array name="config_secondaryBuiltInDisplayCutoutSideOverride" translatable="false">
    </string-array>

    <!-- An array contains unique ids of all built-in displays and the unique id of a display can be
         obtained from {@link Display#getUniqueId}. This array should be set for multi-display
@@ -6415,6 +6429,11 @@
        <item>@string/config_secondaryBuiltInDisplayCutoutRectApproximation</item>
    </string-array>

    <array name="config_displayCutoutSideOverrideArray" translatable="false">
        <item>@array/config_mainBuiltInDisplayCutoutSideOverride</item>
        <item>@array/config_secondaryBuiltInDisplayCutoutSideOverride</item>
    </array>

    <!-- The maskBuiltInDisplayCutout config for each display in a multi-display device. -->
    <array name="config_maskBuiltInDisplayCutoutArray" translatable="false">
        <item>@bool/config_maskMainBuiltInDisplayCutout</item>
+3 −0
Original line number Diff line number Diff line
@@ -4035,6 +4035,7 @@
  <java-symbol type="string" name="global_action_logout" />
  <java-symbol type="string" name="config_mainBuiltInDisplayCutout" />
  <java-symbol type="string" name="config_mainBuiltInDisplayCutoutRectApproximation" />
  <java-symbol type="array" name="config_mainBuiltInDisplayCutoutSideOverride" />
  <java-symbol type="drawable" name="messaging_user" />
  <java-symbol type="bool" name="config_fillMainBuiltInDisplayCutout" />
  <java-symbol type="drawable" name="ic_logout" />
@@ -5001,9 +5002,11 @@
  <java-symbol type="string" name="config_secondaryBuiltInDisplayCutoutRectApproximation" />
  <java-symbol type="bool" name="config_fillSecondaryBuiltInDisplayCutout" />
  <java-symbol type="bool" name="config_maskSecondaryBuiltInDisplayCutout" />
  <java-symbol type="array" name="config_secondaryBuiltInDisplayCutoutSideOverride" />
  <java-symbol type="array" name="config_displayUniqueIdArray" />
  <java-symbol type="array" name="config_displayCutoutPathArray" />
  <java-symbol type="array" name="config_displayCutoutApproximationRectArray" />
  <java-symbol type="array" name="config_displayCutoutSideOverrideArray" />
  <java-symbol type="array" name="config_fillBuiltInDisplayCutoutArray" />
  <java-symbol type="array" name="config_maskBuiltInDisplayCutoutArray" />
  <java-symbol type="dimen" name="secondary_waterfall_display_left_edge_size" />
+107 −25
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.view;

import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.NO_CUTOUT;
import static android.view.DisplayCutout.extractBoundsFromList;
import static android.view.DisplayCutout.fromSpec;
@@ -180,7 +182,7 @@ public class DisplayCutoutTest {
        final int displayHeight = 400;
        final float density = 1f;
        final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight,
                density, Insets.NONE);
                density, Insets.NONE, null);
        assertThat(cutout.getCutoutPath(), notNullValue());
    }

@@ -191,9 +193,9 @@ public class DisplayCutoutTest {
        final int displayHeight = 400;
        final float density = 1f;
        final Path first = fromSpec(cutoutSpecString, displayWidth, displayHeight,
                density, Insets.NONE).getCutoutPath();
                density, Insets.NONE, null).getCutoutPath();
        final Path second = fromSpec(cutoutSpecString, displayWidth, displayHeight,
                density, Insets.NONE).getCutoutPath();
                density, Insets.NONE, null).getCutoutPath();
        assertThat(first, equalTo(second));
    }

@@ -203,9 +205,9 @@ public class DisplayCutoutTest {
        final int displayHeight = 400;
        final float density = 1f;
        final Path first = fromSpec("L1,0 L1,1 L0,1 z", displayWidth, displayHeight,
                density, Insets.NONE).getCutoutPath();
                density, Insets.NONE, null).getCutoutPath();
        final Path second = fromSpec("L2,0 L2,2 L0,2 z", displayWidth, displayHeight,
                density, Insets.NONE).getCutoutPath();
                density, Insets.NONE, null).getCutoutPath();
        assertThat(first, not(equalTo(second)));
    }

@@ -216,7 +218,7 @@ public class DisplayCutoutTest {
        final int displayHeight = 400;
        final float density = 1f;
        final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight,
                density, Insets.NONE);
                density, Insets.NONE, null);
        assertThat(displayWidth, equalTo(cutout.getCutoutPathParserInfo().getDisplayWidth()));
        assertThat(displayHeight, equalTo(cutout.getCutoutPathParserInfo().getDisplayHeight()));
        assertThat(density, equalTo(cutout.getCutoutPathParserInfo().getDensity()));
@@ -360,63 +362,64 @@ public class DisplayCutoutTest {
    @Test
    public void fromSpec_caches() {
        Insets waterfallInsets = Insets.of(0, 20, 0, 20);
        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets);
        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets, null);
        assertThat(
                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets),
                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets, null),
                sameInstance(cached));
    }

    @Test
    public void fromSpec_wontCacheIfSpecChanges() {
        DisplayCutout cached = fromSpec("L1,0 L1000,1000 L0,1 z", 200, 400, 1f, Insets.NONE);
        DisplayCutout cached = fromSpec("L1,0 L1000,1000 L0,1 z", 200, 400, 1f, Insets.NONE, null);
        assertThat(
                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE, null),
                not(sameInstance(cached)));
    }

    @Test
    public void fromSpec_wontCacheIfScreenWidthChanges() {
        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 2000, 400, 1f, Insets.NONE);
        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 2000, 400, 1f, Insets.NONE, null);
        assertThat(
                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE, null),
                not(sameInstance(cached)));
    }

    @Test
    public void fromSpec_wontCacheIfScreenHeightChanges() {
        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 4000, 1f, Insets.NONE);
        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 4000, 1f, Insets.NONE, null);
        assertThat(
                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE, null),
                not(sameInstance(cached)));
    }

    @Test
    public void fromSpec_wontCacheIfDensityChanges() {
        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE);
        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE, null);
        assertThat(
                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE, null),
                not(sameInstance(cached)));
    }

    @Test
    public void fromSpec_wontCacheIfWaterfallInsetsChange() {
        Insets waterfallInsets = Insets.of(0, 20, 0, 20);
        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE);
        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE, null);
        assertThat(
                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, waterfallInsets),
                fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, waterfallInsets, null),
                not(sameInstance(cached)));
    }

    @Test
    public void fromSpec_setsSafeInsets_top() {
        DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z", 200, 400, 2f, Insets.NONE);
        DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z", 200, 400, 2f,
                Insets.NONE, null);
        assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 0)));
    }

    @Test
    public void fromSpec_setsSafeInsets_top_and_bottom() {
        DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
                + "@bottom M -50,0 v -10,0 h 100 v 20 z", 200, 400, 2f, Insets.NONE);
                + "@bottom M -50,0 v -10,0 h 100 v 20 z", 200, 400, 2f, Insets.NONE, null);
        assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 10)));
        assertThat(cutout.getBoundingRectsAll(), equalTo(new Rect[]{
                ZERO_RECT, new Rect(50, 0, 150, 20),
@@ -426,33 +429,35 @@ public class DisplayCutoutTest {

    @Test
    public void fromSpec_setsSafeInsets_waterfallTopBottom() {
        DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(0, 30, 0, 30));
        DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(0, 30, 0, 30), null);
        assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 30, 0, 30)));
    }

    @Test
    public void fromSpec_setsSafeInsets_waterfallLeftRight() {
        DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 0, 30, 0));
        DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 0, 30, 0), null);
        assertThat(cutout.getSafeInsets(), equalTo(new Rect(30, 0, 30, 0)));
    }

    @Test
    public void fromSpec_setsSafeInsets_waterfall_allEdges() {
        DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 30, 30, 30));
        DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 30, 30, 30), null);
        assertThat(cutout.getSafeInsets(), equalTo(new Rect(30, 30, 30, 30)));
    }

    @Test
    public void fromSpec_setsSafeInsets_cutoutTopBottom_waterfallTopBottom() {
        DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
                + "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f, Insets.of(0, 30, 0, 30));
                + "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f,
                Insets.of(0, 30, 0, 30), null);
        assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 30, 0, 30)));
    }

    @Test
    public void fromSpec_setsSafeInsets_cutoutTopBottom_waterfallLeftRight() {
        DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
                + "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f, Insets.of(30, 0, 30, 0));
                + "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f,
                    Insets.of(30, 0, 30, 0), null);
        assertThat(cutout.getSafeInsets(), equalTo(new Rect(30, 20, 30, 20)));
    }

@@ -568,7 +573,84 @@ public class DisplayCutoutTest {
        DisplayCutout rotated = cutout.getRotated(displayH, displayW, ROTATION_90, ROTATION_180);
        assertEquals(expected, rotated);
    }
    @Test
    public void testGetRotatedCutoutWithOverride_top_rot0() {
        int displayW = 500, displayH = 1000;
        final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
                BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
        DisplayCutout expected = new DisplayCutout(Insets.of(20, 100, 20, 0),
                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
                Insets.of(20, 0, 20, 0), null, sideOverrides);
        DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
                Insets.of(20, 0, 20, 0), null, sideOverrides);
        DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_0);
        assertEquals(expected, rotated);
    }

    @Test
    public void testGetRotatedCutoutWithOverride_top_rot90() {
        int displayW = 500, displayH = 1000;
        final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
                BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
        DisplayCutout expected = new DisplayCutout(Insets.of(0, 20, 0, 75),
                ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(0, displayW - 75, 100, displayW - 50),
                Insets.of(0, 20, 0, 20), createParserInfo(ROTATION_90), sideOverrides);
        DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
                Insets.of(20, 0, 20, 0), null, sideOverrides);
        DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_90);
        assertEquals(expected, rotated);
    }

    @Test
    public void testGetRotatedCutoutWithOverride_top_rot180() {
        int displayW = 500, displayH = 1000;
        final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
                BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
        DisplayCutout expected = new DisplayCutout(Insets.of(20, 0, 20, 100),
                ZERO_RECT, ZERO_RECT, ZERO_RECT,
                new Rect(displayW - 75, displayH - 100, displayW - 50, displayH - 0),
                Insets.of(20, 0, 20, 0), createParserInfo(ROTATION_180), sideOverrides);
        DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
                Insets.of(20, 0, 20, 0), null, sideOverrides);
        DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_180);
        assertEquals(expected, rotated);
    }

    @Test
    public void testGetRotatedCutoutWithOverride_top_rot270() {
        int displayW = 500, displayH = 1000;
        final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
                BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
        DisplayCutout expected = new DisplayCutout(Insets.of(0, 75, 0, 20),
                ZERO_RECT, new Rect(displayH - 100, 50, displayH - 0, 75), ZERO_RECT, ZERO_RECT,
                Insets.of(0, 20, 0, 20), createParserInfo(ROTATION_270), sideOverrides);
        DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
                Insets.of(20, 0, 20, 0), null, sideOverrides);
        DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_270);
        assertEquals(expected, rotated);
    }

    @Test
    public void testGetRotatedCutoutWithOverride_top_rot90to180() {
        int displayW = 500, displayH = 1000;
        final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
                BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
        DisplayCutout expected = new DisplayCutout(Insets.of(20, 0, 20, 100),
                ZERO_RECT, ZERO_RECT, ZERO_RECT,
                new Rect(displayW - 75, displayH - 100, displayW - 50, displayH - 0),
                Insets.of(20, 0, 20, 0), createParserInfo(ROTATION_180),
                sideOverrides);
        DisplayCutout cutout = new DisplayCutout(Insets.of(0, 20, 0, 75),
                ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(0, displayW - 75, 100, displayW - 50),
                Insets.of(0, 20, 0, 20), null, sideOverrides);
        // starting from 90, so the start displayW/H are swapped:
        DisplayCutout rotated = cutout.getRotated(displayH, displayW, ROTATION_90, ROTATION_180);
        assertEquals(expected, rotated);
    }
    private static DisplayCutout createCutoutTop() {
        return createCutoutWithInsets(0, 100, 0, 0);
    }