DisplayCutout: Move emulation into resource overlay

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
This commit is contained in:
Adrian Roos
2018-01-05 16:14:34 +01:00
parent 1cf585059c
commit 30f5321310
11 changed files with 153 additions and 83 deletions

View File

@@ -2776,6 +2776,11 @@
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). -->

View File

@@ -3199,6 +3199,7 @@
<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" />

View 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();

View File

@@ -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)

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View 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.
*/

View 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();

View 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);

View 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. */