OSDN Git Service

DisplayCutout: Move emulation into resource overlay
authorAdrian Roos <roosa@google.com>
Fri, 5 Jan 2018 15:14:34 +0000 (16:14 +0100)
committerAdrian Roos <roosa@google.com>
Mon, 15 Jan 2018 13:44:41 +0000 (14:44 +0100)
Bug: 65689439
Test: atest PhoneWindowManagerLayoutTest
Test: adb shell cmd overlay enable com.android.internal.display.cutout.emulation && adb shell stop && adb shell start
Change-Id: I7a319c28da53f78f884556cf176e01321b9c6b55

core/res/res/values/config.xml
core/res/res/values/symbols.xml
packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
packages/overlays/DisplayCutoutEmulationOverlay/Android.mk [new file with mode: 0644]
packages/overlays/DisplayCutoutEmulationOverlay/AndroidManifest.xml [new file with mode: 0644]
packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml [new file with mode: 0644]
packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml [new file with mode: 0644]
services/core/java/com/android/server/display/LocalDisplayAdapter.java
services/core/java/com/android/server/policy/PhoneWindowManager.java
services/core/java/com/android/server/wm/DisplayFrames.java
services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java

index 2440e9b..699285a 100644 (file)
          the display's native orientation. -->
     <string translatable="false" name="config_mainBuiltInDisplayCutout"></string>
 
+    <!-- Whether the display cutout region of the main built-in display should be forced to
+         black in software (to avoid aliasing or emulate a cutout that is not physically existent).
+         -->
+    <bool name="config_fillMainBuiltInDisplayCutout">false</bool>
+
     <!-- Ultrasound support for Mic/speaker path -->
     <!-- Whether the default microphone audio source supports near-ultrasound frequencies
          (range of 18 - 21 kHz). -->
index fb7baf6..c5429d8 100644 (file)
 
   <java-symbol type="string" name="global_action_logout" />
   <java-symbol type="string" name="config_mainBuiltInDisplayCutout" />
+  <java-symbol type="bool" name="config_fillMainBuiltInDisplayCutout" />
   <java-symbol type="drawable" name="ic_logout" />
 
   <java-symbol type="array" name="config_autoBrightnessDisplayValuesNits" />
index a102260..f41425a 100644 (file)
@@ -38,6 +38,9 @@ import android.view.ViewGroup.LayoutParams;
 import android.view.WindowInsets;
 import android.view.WindowManager;
 
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+
 import java.util.Collections;
 import java.util.List;
 
@@ -45,18 +48,28 @@ import java.util.List;
  * Emulates a display cutout by drawing its shape in an overlay as supplied by
  * {@link DisplayCutout}.
  */
-public class EmulatedDisplayCutout extends SystemUI {
+public class EmulatedDisplayCutout extends SystemUI implements ConfigurationListener {
     private View mOverlay;
     private boolean mAttached;
     private WindowManager mWindowManager;
 
     @Override
     public void start() {
+        Dependency.get(ConfigurationController.class).addCallback(this);
+
         mWindowManager = mContext.getSystemService(WindowManager.class);
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.EMULATE_DISPLAY_CUTOUT),
-                false, mObserver, UserHandle.USER_ALL);
-        mObserver.onChange(false);
+        updateAttached();
+    }
+
+    @Override
+    public void onOverlayChanged() {
+        updateAttached();
+    }
+
+    private void updateAttached() {
+        boolean shouldAttach = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
+        setAttached(shouldAttach);
     }
 
     private void setAttached(boolean attached) {
@@ -94,17 +107,6 @@ public class EmulatedDisplayCutout extends SystemUI {
         return lp;
     }
 
-    private ContentObserver mObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
-        @Override
-        public void onChange(boolean selfChange) {
-            boolean emulateCutout = Settings.Global.getInt(
-                    mContext.getContentResolver(), Settings.Global.EMULATE_DISPLAY_CUTOUT,
-                    Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF)
-                    != Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF;
-            setAttached(emulateCutout);
-        }
-    };
-
     private static class CutoutView extends View {
         private final Paint mPaint = new Paint();
         private final Path mBounds = new Path();
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
new file mode 100644 (file)
index 0000000..f4205ad
--- /dev/null
@@ -0,0 +1,13 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := DisplayCutoutEmulation
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := DisplayCutoutEmulationOverlay
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationOverlay/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..dd43690
--- /dev/null
@@ -0,0 +1,8 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.internal.display.cutout.emulation"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <overlay android:targetPackage="android" android:priority="1"/>
+
+    <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
new file mode 100644 (file)
index 0000000..88c19c7
--- /dev/null
@@ -0,0 +1,44 @@
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- The bounding path of the cutout region of the main built-in display.
+         Must either be empty if there is no cutout region, or a string that is parsable by
+         {@link android.util.PathParser}. -->
+    <string translatable="false" name="config_mainBuiltInDisplayCutout">
+        M 687.0,0
+        l -66,50
+        l 0,50
+        l 66,50
+        l 66,0
+        l 66,-50
+        l 0,-50
+        l -66,-50
+        z
+    </string>
+
+    <!-- Whether the display cutout region of the main built-in display should be forced to
+     black in software (to avoid aliasing or emulate a cutout that is not physically existent).
+     -->
+    <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
+
+    <!-- Height of the status bar -->
+    <dimen name="status_bar_height">150px</dimen>
+
+</resources>
+
+
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml
new file mode 100644 (file)
index 0000000..5d5c425
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <string name="display_cutout_emulation_overlay">Display Cutout Emulation</string>
+
+</resources>
+
index 483b02c..23e4c9b 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.server.display;
 
+import android.app.ActivityThread;
 import android.content.res.Resources;
 import com.android.server.LocalServices;
 import com.android.server.lights.Light;
@@ -392,7 +393,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
                             | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
                 }
 
-                final Resources res = getContext().getResources();
+                final Resources res = getOverlayContext().getResources();
                 if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
                     mInfo.name = res.getString(
                             com.android.internal.R.string.display_manager_built_in_display_name);
@@ -687,6 +688,11 @@ final class LocalDisplayAdapter extends DisplayAdapter {
         }
     }
 
+    /** Supplies a context whose Resources apply runtime-overlays */
+    Context getOverlayContext() {
+        return ActivityThread.currentActivityThread().getSystemUiContext();
+    }
+
     /**
      * Keeps track of a display configuration.
      */
index 076c0e4..1bbfe2a 100644 (file)
@@ -601,8 +601,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
 
     PointerLocationView mPointerLocationView;
 
-    boolean mEmulateDisplayCutout = false;
-
     // During layout, the layer at which the doc window is placed.
     int mDockLayer;
     // During layout, this is the layer of the status bar.
@@ -955,9 +953,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
             resolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.POLICY_CONTROL), false, this,
                     UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.EMULATE_DISPLAY_CUTOUT), false, this,
-                    UserHandle.USER_ALL);
             updateSettings();
         }
 
@@ -2344,10 +2339,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
             if (mImmersiveModeConfirmation != null) {
                 mImmersiveModeConfirmation.loadSetting(mCurrentUserId);
             }
-            mEmulateDisplayCutout = Settings.Global.getInt(resolver,
-                    Settings.Global.EMULATE_DISPLAY_CUTOUT,
-                    Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF)
-                    != Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF;
         }
         synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
             PolicyControl.reloadFromSetting(mContext);
@@ -4382,7 +4373,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
     /** {@inheritDoc} */
     @Override
     public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
-        displayFrames.onBeginLayout(mEmulateDisplayCutout, mStatusBarHeight);
+        displayFrames.onBeginLayout();
         // TODO(multi-display): This doesn't seem right...Maybe only apply to default display?
         mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
         mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
index bd06192..13d0c86 100644 (file)
@@ -101,7 +101,7 @@ public class DisplayFrames {
     /** During layout, the current screen borders along which input method windows are placed. */
     public final Rect mDock = new Rect();
 
-    /** The display cutout used for layout (after rotation and emulation) */
+    /** The display cutout used for layout (after rotation) */
     @NonNull public DisplayCutout mDisplayCutout = DisplayCutout.NO_CUTOUT;
 
     /** The cutout as supplied by display info */
@@ -134,7 +134,7 @@ public class DisplayFrames {
                 ? info.displayCutout : DisplayCutout.NO_CUTOUT;
     }
 
-    public void onBeginLayout(boolean emulateDisplayCutout, int statusBarHeight) {
+    public void onBeginLayout() {
         switch (mRotation) {
             case ROTATION_90:
                 mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.top;
@@ -172,12 +172,8 @@ public class DisplayFrames {
         mStable.set(mUnrestricted);
         mStableFullscreen.set(mUnrestricted);
         mCurrent.set(mUnrestricted);
-        mDisplayCutout = mDisplayInfoCutout;
-        if (emulateDisplayCutout) {
-            setEmulatedDisplayCutout((int) (statusBarHeight * 0.8));
-        }
-        mDisplayCutout = mDisplayCutout.calculateRelativeTo(mOverscan);
 
+        mDisplayCutout = mDisplayInfoCutout.calculateRelativeTo(mOverscan);
         mDisplayCutoutSafe.set(Integer.MIN_VALUE, Integer.MIN_VALUE,
                 Integer.MAX_VALUE, Integer.MAX_VALUE);
         if (!mDisplayCutout.isEmpty()) {
@@ -201,51 +197,6 @@ public class DisplayFrames {
         return mDock.bottom - mCurrent.bottom;
     }
 
-    private void setEmulatedDisplayCutout(int height) {
-        final boolean swappedDimensions = mRotation == ROTATION_90 || mRotation == ROTATION_270;
-
-        final int screenWidth = swappedDimensions ? mDisplayHeight : mDisplayWidth;
-        final int screenHeight = swappedDimensions ? mDisplayWidth : mDisplayHeight;
-
-        final int widthTop = (int) (screenWidth * 0.3);
-        final int widthBottom = widthTop - height;
-
-        switch (mRotation) {
-            case ROTATION_90:
-                mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList(
-                        new Point(0, (screenWidth - widthTop) / 2),
-                        new Point(height, (screenWidth - widthBottom) / 2),
-                        new Point(height, (screenWidth + widthBottom) / 2),
-                        new Point(0, (screenWidth + widthTop) / 2)
-                ));
-                break;
-            case ROTATION_180:
-                mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList(
-                        new Point((screenWidth - widthTop) / 2, screenHeight),
-                        new Point((screenWidth - widthBottom) / 2, screenHeight - height),
-                        new Point((screenWidth + widthBottom) / 2, screenHeight - height),
-                        new Point((screenWidth + widthTop) / 2, screenHeight)
-                ));
-                break;
-            case ROTATION_270:
-                mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList(
-                        new Point(screenHeight, (screenWidth - widthTop) / 2),
-                        new Point(screenHeight - height, (screenWidth - widthBottom) / 2),
-                        new Point(screenHeight - height, (screenWidth + widthBottom) / 2),
-                        new Point(screenHeight, (screenWidth + widthTop) / 2)
-                ));
-                break;
-            default:
-                mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList(
-                        new Point((screenWidth - widthTop) / 2, 0),
-                        new Point((screenWidth - widthBottom) / 2, height),
-                        new Point((screenWidth + widthBottom) / 2, height),
-                        new Point((screenWidth + widthTop) / 2, 0)
-                ));
-                break;
-        }
-    }
-
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         mStable.writeToProto(proto, STABLE_BOUNDS);
index e7e9aba..ad89953 100644 (file)
@@ -24,6 +24,8 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
+import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.mock;
 
@@ -31,6 +33,8 @@ import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.graphics.Matrix;
+import android.graphics.Path;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.IBinder;
@@ -38,6 +42,7 @@ import android.os.UserHandle;
 import android.support.test.InstrumentationRegistry;
 import android.testing.TestableResources;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.View;
@@ -65,6 +70,9 @@ public class PhoneWindowManagerTestBase {
 
     FakeWindowState mStatusBar;
     FakeWindowState mNavigationBar;
+    private boolean mHasDisplayCutout;
+    private int mRotation = ROTATION_0;
+    private final Matrix mTmpMatrix = new Matrix();
 
     @Before
     public void setUpBase() throws Exception {
@@ -80,16 +88,32 @@ public class PhoneWindowManagerTestBase {
 
         mPolicy = TestablePhoneWindowManager.create(mContext);
 
-        setRotation(ROTATION_0);
+        updateDisplayFrames();
     }
 
     public void setRotation(int rotation) {
+        mRotation = rotation;
+        updateDisplayFrames();
+    }
+
+    private void updateDisplayFrames() {
         DisplayInfo info = new DisplayInfo();
 
-        final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270;
+        final boolean flippedDimensions = mRotation == ROTATION_90 || mRotation == ROTATION_270;
         info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
         info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
-        info.rotation = rotation;
+        info.rotation = mRotation;
+        if (mHasDisplayCutout) {
+            Path p = new Path();
+            p.addRect(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT,
+                    Path.Direction.CCW);
+            transformPhysicalToLogicalCoordinates(
+                    mRotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, mTmpMatrix);
+            p.transform(mTmpMatrix);
+            info.displayCutout = DisplayCutout.fromBounds(p);
+        } else {
+            info.displayCutout = null;
+        }
 
         mFrames = new DisplayFrames(Display.DEFAULT_DISPLAY, info);
     }
@@ -116,7 +140,8 @@ public class PhoneWindowManagerTestBase {
     }
 
     public void addDisplayCutout() {
-        mPolicy.mEmulateDisplayCutout = true;
+        mHasDisplayCutout = true;
+        updateDisplayFrames();
     }
 
     /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */