Merge emulated cutout into either rounded corner overlay
This so that we can save a layer, which avoids dropping us into
GL composition during animations.
This assumes that the cutout is always at the top or bottom edge
in the device's natural orientation.
Note that the two overlays for the top and bottom rounded corners
are still separate.
Bug: 72492508
Test: enable emulated cutout, verify it still shows up
Change-Id: I895084828e0502005bfa31e37d23dd3a6f01a2ca
diff --git a/packages/SystemUI/res/layout/rounded_corners.xml b/packages/SystemUI/res/layout/rounded_corners.xml
index d1ef5d6..734c877 100644
--- a/packages/SystemUI/res/layout/rounded_corners.xml
+++ b/packages/SystemUI/res/layout/rounded_corners.xml
@@ -22,7 +22,7 @@
android:id="@+id/left"
android:layout_width="12dp"
android:layout_height="12dp"
- android:layout_gravity="left"
+ android:layout_gravity="left|top"
android:tint="#ff000000"
android:src="@drawable/rounded" />
<ImageView
@@ -30,6 +30,6 @@
android:layout_width="12dp"
android:layout_height="12dp"
android:tint="#ff000000"
- android:layout_gravity="right"
+ android:layout_gravity="right|bottom"
android:src="@drawable/rounded" />
</FrameLayout>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 6768470..1cc1cc8 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -348,8 +348,7 @@
<item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
<item>com.android.systemui.LatencyTester</item>
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>
- <item>com.android.systemui.RoundedCorners</item>
- <item>com.android.systemui.EmulatedDisplayCutout</item>
+ <item>com.android.systemui.ScreenDecorations</item>
<item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
</string-array>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index edda613..f9aa821 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -96,5 +96,7 @@
<!-- For StatusBarIconContainer to tag its icon views -->
<item type="id" name="status_bar_view_state_tag" />
+
+ <item type="id" name="display_cutout" />
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
deleted file mode 100644
index 5d2e4d0..0000000
--- a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui;
-
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.view.DisplayCutout;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-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;
-
-/**
- * Emulates a display cutout by drawing its shape in an overlay as supplied by
- * {@link DisplayCutout}.
- */
-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);
- 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) {
- if (attached && !mAttached) {
- if (mOverlay == null) {
- mOverlay = new CutoutView(mContext);
- mOverlay.setLayoutParams(getLayoutParams());
- }
- mWindowManager.addView(mOverlay, mOverlay.getLayoutParams());
- mAttached = true;
- } else if (!attached && mAttached) {
- mWindowManager.removeView(mOverlay);
- mAttached = false;
- }
- }
-
- private WindowManager.LayoutParams getLayoutParams() {
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_SLIPPERY
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR,
- PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
- | WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
- lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- lp.setTitle("EmulatedDisplayCutout");
- lp.gravity = Gravity.TOP;
- return lp;
- }
-
- private static class CutoutView extends View {
- private final Paint mPaint = new Paint();
- private final Path mBounds = new Path();
-
- CutoutView(Context context) {
- super(context);
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- mBounds.reset();
- if (insets.getDisplayCutout() != null) {
- insets.getDisplayCutout().getBounds().getBoundaryPath(mBounds);
- }
- invalidate();
- return insets.consumeDisplayCutout();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- if (!mBounds.isEmpty()) {
- mPaint.setColor(Color.BLACK);
- mPaint.setStyle(Paint.Style.FILL);
-
- canvas.drawPath(mBounds, mPaint);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
deleted file mode 100644
index c960fa1..0000000
--- a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui;
-
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-
-import static com.android.systemui.tuner.TunablePadding.FLAG_START;
-import static com.android.systemui.tuner.TunablePadding.FLAG_END;
-
-import android.app.Fragment;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.provider.Settings.Secure;
-import android.support.annotation.VisibleForTesting;
-import android.util.DisplayMetrics;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnLayoutChangeListener;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
-import android.widget.ImageView;
-
-import com.android.systemui.R.id;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
-import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.SecureSetting;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.NavigationBarFragment;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.tuner.TunablePadding;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
-
-public class RoundedCorners extends SystemUI implements Tunable {
- public static final String SIZE = "sysui_rounded_size";
- public static final String PADDING = "sysui_rounded_content_padding";
-
- private int mRoundedDefault;
- private View mOverlay;
- private View mBottomOverlay;
- private float mDensity;
- private TunablePadding mQsPadding;
- private TunablePadding mStatusBarPadding;
- private TunablePadding mNavBarPadding;
-
- @Override
- public void start() {
- mRoundedDefault = mContext.getResources().getDimensionPixelSize(
- R.dimen.rounded_corner_radius);
- if (mRoundedDefault != 0) {
- setupRounding();
- }
- int padding = mContext.getResources().getDimensionPixelSize(
- R.dimen.rounded_corner_content_padding);
- if (padding != 0) {
- setupPadding(padding);
- }
- }
-
- private void setupRounding() {
- mOverlay = LayoutInflater.from(mContext)
- .inflate(R.layout.rounded_corners, null);
- mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- mOverlay.setAlpha(0);
- mOverlay.findViewById(R.id.right).setRotation(90);
-
- mContext.getSystemService(WindowManager.class)
- .addView(mOverlay, getWindowLayoutParams());
- mBottomOverlay = LayoutInflater.from(mContext)
- .inflate(R.layout.rounded_corners, null);
- mBottomOverlay.setAlpha(0);
- mBottomOverlay.findViewById(R.id.right).setRotation(180);
- mBottomOverlay.findViewById(R.id.left).setRotation(270);
- WindowManager.LayoutParams layoutParams = getWindowLayoutParams();
- layoutParams.gravity = Gravity.BOTTOM;
- mContext.getSystemService(WindowManager.class)
- .addView(mBottomOverlay, layoutParams);
-
- DisplayMetrics metrics = new DisplayMetrics();
- mContext.getSystemService(WindowManager.class)
- .getDefaultDisplay().getMetrics(metrics);
- mDensity = metrics.density;
-
- Dependency.get(TunerService.class).addTunable(this, SIZE);
-
- // Watch color inversion and invert the overlay as needed.
- SecureSetting setting = new SecureSetting(mContext, Dependency.get(Dependency.MAIN_HANDLER),
- Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
- @Override
- protected void handleValueChanged(int value, boolean observedChange) {
- int tint = value != 0 ? Color.WHITE : Color.BLACK;
- ColorStateList tintList = ColorStateList.valueOf(tint);
- ((ImageView) mOverlay.findViewById(id.left)).setImageTintList(tintList);
- ((ImageView) mOverlay.findViewById(id.right)).setImageTintList(tintList);
- ((ImageView) mBottomOverlay.findViewById(id.left)).setImageTintList(tintList);
- ((ImageView) mBottomOverlay.findViewById(id.right)).setImageTintList(tintList);
- }
- };
- setting.setListening(true);
- setting.onChange(false);
-
- mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
- mOverlay.removeOnLayoutChangeListener(this);
- mOverlay.animate()
- .alpha(1)
- .setDuration(1000)
- .start();
- mBottomOverlay.animate()
- .alpha(1)
- .setDuration(1000)
- .start();
- }
- });
- }
-
- private void setupPadding(int padding) {
- // Add some padding to all the content near the edge of the screen.
- StatusBar sb = getComponent(StatusBar.class);
- View statusBar = (sb != null ? sb.getStatusBarWindow() : null);
- if (statusBar != null) {
- TunablePadding.addTunablePadding(statusBar.findViewById(R.id.keyguard_header), PADDING,
- padding, FLAG_END);
-
- FragmentHostManager fragmentHostManager = FragmentHostManager.get(statusBar);
- fragmentHostManager.addTagListener(CollapsedStatusBarFragment.TAG,
- new TunablePaddingTagListener(padding, R.id.status_bar));
- fragmentHostManager.addTagListener(QS.TAG,
- new TunablePaddingTagListener(padding, R.id.header));
- }
- }
-
- private WindowManager.LayoutParams getWindowLayoutParams() {
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_SLIPPERY
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
- PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
- | WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
- lp.setTitle("RoundedOverlay");
- lp.gravity = Gravity.TOP;
- lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- return lp;
- }
-
-
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (mOverlay == null) return;
- if (SIZE.equals(key)) {
- int size = mRoundedDefault;
- try {
- size = (int) (Integer.parseInt(newValue) * mDensity);
- } catch (Exception e) {
- }
- setSize(mOverlay.findViewById(R.id.left), size);
- setSize(mOverlay.findViewById(R.id.right), size);
- setSize(mBottomOverlay.findViewById(R.id.left), size);
- setSize(mBottomOverlay.findViewById(R.id.right), size);
- }
- }
-
- private void setSize(View view, int pixelSize) {
- LayoutParams params = view.getLayoutParams();
- params.width = pixelSize;
- params.height = pixelSize;
- view.setLayoutParams(params);
- }
-
- @VisibleForTesting
- static class TunablePaddingTagListener implements FragmentListener {
-
- private final int mPadding;
- private final int mId;
- private TunablePadding mTunablePadding;
-
- public TunablePaddingTagListener(int padding, int id) {
- mPadding = padding;
- mId = id;
- }
-
- @Override
- public void onFragmentViewCreated(String tag, Fragment fragment) {
- if (mTunablePadding != null) {
- mTunablePadding.destroy();
- }
- View view = fragment.getView();
- if (mId != 0) {
- view = view.findViewById(mId);
- }
- mTunablePadding = TunablePadding.addTunablePadding(view, PADDING, mPadding,
- FLAG_START | FLAG_END);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
new file mode 100644
index 0000000..0b3e9e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -0,0 +1,415 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import static com.android.systemui.tuner.TunablePadding.FLAG_START;
+import static com.android.systemui.tuner.TunablePadding.FLAG_END;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.provider.Settings.Secure;
+import android.support.annotation.VisibleForTesting;
+import android.util.DisplayMetrics;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnLayoutChangeListener;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.tuner.TunablePadding;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerService.Tunable;
+
+/**
+ * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
+ * for antialiasing and emulation purposes.
+ */
+public class ScreenDecorations extends SystemUI implements Tunable {
+ public static final String SIZE = "sysui_rounded_size";
+ public static final String PADDING = "sysui_rounded_content_padding";
+
+ private int mRoundedDefault;
+ private View mOverlay;
+ private View mBottomOverlay;
+ private float mDensity;
+ private WindowManager mWindowManager;
+ private boolean mLandscape;
+
+ @Override
+ public void start() {
+ mWindowManager = mContext.getSystemService(WindowManager.class);
+ mRoundedDefault = mContext.getResources().getDimensionPixelSize(
+ R.dimen.rounded_corner_radius);
+ if (mRoundedDefault != 0 || shouldDrawCutout()) {
+ setupDecorations();
+ }
+ int padding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.rounded_corner_content_padding);
+ if (padding != 0) {
+ setupPadding(padding);
+ }
+ }
+
+ private void setupDecorations() {
+ mOverlay = LayoutInflater.from(mContext)
+ .inflate(R.layout.rounded_corners, null);
+ ((ViewGroup)mOverlay).addView(new DisplayCutoutView(mContext, true,
+ this::updateWindowVisibilities));
+ mBottomOverlay = LayoutInflater.from(mContext)
+ .inflate(R.layout.rounded_corners, null);
+ ((ViewGroup)mBottomOverlay).addView(new DisplayCutoutView(mContext, false,
+ this::updateWindowVisibilities));
+
+ mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ mOverlay.setAlpha(0);
+
+ mBottomOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ mBottomOverlay.setAlpha(0);
+
+ updateViews();
+
+ mWindowManager.addView(mOverlay, getWindowLayoutParams());
+ mWindowManager.addView(mBottomOverlay, getBottomLayoutParams());
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ mWindowManager.getDefaultDisplay().getMetrics(metrics);
+ mDensity = metrics.density;
+
+ Dependency.get(TunerService.class).addTunable(this, SIZE);
+
+ // Watch color inversion and invert the overlay as needed.
+ SecureSetting setting = new SecureSetting(mContext, Dependency.get(Dependency.MAIN_HANDLER),
+ Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
+ @Override
+ protected void handleValueChanged(int value, boolean observedChange) {
+ int tint = value != 0 ? Color.WHITE : Color.BLACK;
+ ColorStateList tintList = ColorStateList.valueOf(tint);
+ ((ImageView) mOverlay.findViewById(R.id.left)).setImageTintList(tintList);
+ ((ImageView) mOverlay.findViewById(R.id.right)).setImageTintList(tintList);
+ ((ImageView) mBottomOverlay.findViewById(R.id.left)).setImageTintList(tintList);
+ ((ImageView) mBottomOverlay.findViewById(R.id.right)).setImageTintList(tintList);
+ }
+ };
+ setting.setListening(true);
+ setting.onChange(false);
+
+ mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ mOverlay.removeOnLayoutChangeListener(this);
+ mOverlay.animate()
+ .alpha(1)
+ .setDuration(1000)
+ .start();
+ mBottomOverlay.animate()
+ .alpha(1)
+ .setDuration(1000)
+ .start();
+ }
+ });
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ boolean newLanscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
+ if (newLanscape != mLandscape) {
+ mLandscape = newLanscape;
+
+ if (mOverlay != null) {
+ updateLayoutParams();
+ updateViews();
+ }
+ }
+ if (shouldDrawCutout() && mOverlay == null) {
+ setupDecorations();
+ }
+ }
+
+ private void updateViews() {
+ View topLeft = mOverlay.findViewById(R.id.left);
+ View topRight = mOverlay.findViewById(R.id.right);
+ View bottomLeft = mBottomOverlay.findViewById(R.id.left);
+ View bottomRight = mBottomOverlay.findViewById(R.id.right);
+ if (mLandscape) {
+ // Flip corners
+ View tmp = topRight;
+ topRight = bottomLeft;
+ bottomLeft = tmp;
+ }
+ updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0);
+ updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90);
+ updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
+ updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
+
+ updateWindowVisibilities();
+ }
+
+ private void updateView(View v, int gravity, int rotation) {
+ ((FrameLayout.LayoutParams)v.getLayoutParams()).gravity = gravity;
+ v.setRotation(rotation);
+ }
+
+ private void updateWindowVisibilities() {
+ updateWindowVisibility(mOverlay);
+ updateWindowVisibility(mBottomOverlay);
+ }
+
+ private void updateWindowVisibility(View overlay) {
+ boolean visibleForCutout = shouldDrawCutout()
+ && overlay.findViewById(R.id.display_cutout).getVisibility() == View.VISIBLE;
+ boolean visibleForRoundedCorners = mRoundedDefault > 0;
+ overlay.setVisibility(visibleForCutout || visibleForRoundedCorners
+ ? View.VISIBLE : View.GONE);
+ }
+
+ private boolean shouldDrawCutout() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
+ }
+
+ private void setupPadding(int padding) {
+ // Add some padding to all the content near the edge of the screen.
+ StatusBar sb = getComponent(StatusBar.class);
+ View statusBar = (sb != null ? sb.getStatusBarWindow() : null);
+ if (statusBar != null) {
+ TunablePadding.addTunablePadding(statusBar.findViewById(R.id.keyguard_header), PADDING,
+ padding, FLAG_END);
+
+ FragmentHostManager fragmentHostManager = FragmentHostManager.get(statusBar);
+ fragmentHostManager.addTagListener(CollapsedStatusBarFragment.TAG,
+ new TunablePaddingTagListener(padding, R.id.status_bar));
+ fragmentHostManager.addTagListener(QS.TAG,
+ new TunablePaddingTagListener(padding, R.id.header));
+ }
+ }
+
+ @VisibleForTesting
+ WindowManager.LayoutParams getWindowLayoutParams() {
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_SLIPPERY
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+ PixelFormat.TRANSLUCENT);
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
+ | WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
+ lp.setTitle("ScreenDecorOverlay");
+ lp.gravity = Gravity.TOP | Gravity.LEFT;
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ if (mLandscape) {
+ lp.width = WRAP_CONTENT;
+ lp.height = MATCH_PARENT;
+ }
+ return lp;
+ }
+
+ private WindowManager.LayoutParams getBottomLayoutParams() {
+ WindowManager.LayoutParams lp = getWindowLayoutParams();
+ lp.setTitle("ScreenDecorOverlayBottom");
+ lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+ return lp;
+ }
+
+ private void updateLayoutParams() {
+ mWindowManager.updateViewLayout(mOverlay, getWindowLayoutParams());
+ mWindowManager.updateViewLayout(mBottomOverlay, getBottomLayoutParams());
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ if (mOverlay == null) return;
+ if (SIZE.equals(key)) {
+ int size = mRoundedDefault;
+ try {
+ size = (int) (Integer.parseInt(newValue) * mDensity);
+ } catch (Exception e) {
+ }
+ setSize(mOverlay.findViewById(R.id.left), size);
+ setSize(mOverlay.findViewById(R.id.right), size);
+ setSize(mBottomOverlay.findViewById(R.id.left), size);
+ setSize(mBottomOverlay.findViewById(R.id.right), size);
+ }
+ }
+
+ private void setSize(View view, int pixelSize) {
+ LayoutParams params = view.getLayoutParams();
+ params.width = pixelSize;
+ params.height = pixelSize;
+ view.setLayoutParams(params);
+ }
+
+ @VisibleForTesting
+ static class TunablePaddingTagListener implements FragmentListener {
+
+ private final int mPadding;
+ private final int mId;
+ private TunablePadding mTunablePadding;
+
+ public TunablePaddingTagListener(int padding, int id) {
+ mPadding = padding;
+ mId = id;
+ }
+
+ @Override
+ public void onFragmentViewCreated(String tag, Fragment fragment) {
+ if (mTunablePadding != null) {
+ mTunablePadding.destroy();
+ }
+ View view = fragment.getView();
+ if (mId != 0) {
+ view = view.findViewById(mId);
+ }
+ mTunablePadding = TunablePadding.addTunablePadding(view, PADDING, mPadding,
+ FLAG_START | FLAG_END);
+ }
+ }
+
+ public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener {
+
+ private final DisplayInfo mInfo = new DisplayInfo();
+ private final Paint mPaint = new Paint();
+ private final Rect mBoundingRect = new Rect();
+ private final Path mBoundingPath = new Path();
+ private final int[] mLocation = new int[2];
+ private final boolean mStart;
+ private final Runnable mVisibilityChangedListener;
+
+ public DisplayCutoutView(Context context, boolean start,
+ Runnable visibilityChangedListener) {
+ super(context);
+ mStart = start;
+ mVisibilityChangedListener = visibilityChangedListener;
+ setId(R.id.display_cutout);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
+ getHandler());
+ update();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ getLocationOnScreen(mLocation);
+ canvas.translate(-mLocation[0], -mLocation[1]);
+ if (!mBoundingPath.isEmpty()) {
+ mPaint.setColor(Color.BLACK);
+ mPaint.setStyle(Paint.Style.FILL);
+ canvas.drawPath(mBoundingPath, mPaint);
+ }
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == getDisplay().getDisplayId()) {
+ update();
+ }
+ }
+
+ private void update() {
+ requestLayout();
+ getDisplay().getDisplayInfo(mInfo);
+ mBoundingRect.setEmpty();
+ mBoundingPath.reset();
+ int newVisible;
+ if (hasCutout()) {
+ mBoundingRect.set(mInfo.displayCutout.getBoundingRect());
+ mInfo.displayCutout.getBounds().getBoundaryPath(mBoundingPath);
+ newVisible = VISIBLE;
+ } else {
+ newVisible = GONE;
+ }
+ if (newVisible != getVisibility()) {
+ setVisibility(newVisible);
+ mVisibilityChangedListener.run();
+ }
+ }
+
+ private boolean hasCutout() {
+ if (mInfo.displayCutout == null) {
+ return false;
+ }
+ DisplayCutout displayCutout = mInfo.displayCutout.calculateRelativeTo(
+ new Rect(0, 0, mInfo.logicalWidth, mInfo.logicalHeight));
+ if (mStart) {
+ return displayCutout.getSafeInsetLeft() > 0
+ || displayCutout.getSafeInsetTop() > 0;
+ } else {
+ return displayCutout.getSafeInsetRight() > 0
+ || displayCutout.getSafeInsetBottom() > 0;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mBoundingRect.isEmpty()) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+ setMeasuredDimension(
+ resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0),
+ resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0));
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
similarity index 66%
rename from packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 2a44771..2f05b06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -14,9 +14,13 @@
package com.android.systemui;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
+
import static com.android.systemui.tuner.TunablePadding.FLAG_END;
import static com.android.systemui.tuner.TunablePadding.FLAG_START;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -29,6 +33,7 @@
import static org.mockito.Mockito.when;
import android.app.Fragment;
+import android.content.res.Configuration;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.view.Display;
@@ -36,7 +41,7 @@
import android.view.WindowManager;
import com.android.systemui.R.dimen;
-import com.android.systemui.RoundedCorners.TunablePaddingTagListener;
+import com.android.systemui.ScreenDecorations.TunablePaddingTagListener;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -51,9 +56,9 @@
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class RoundedCornersTest extends SysuiTestCase {
+public class ScreenDecorationsTest extends SysuiTestCase {
- private RoundedCorners mRoundedCorners;
+ private ScreenDecorations mScreenDecorations;
private StatusBar mStatusBar;
private WindowManager mWindowManager;
private FragmentService mFragmentService;
@@ -81,20 +86,22 @@
mTunerService = mDependency.injectMockDependency(TunerService.class);
- mRoundedCorners = new RoundedCorners();
- mRoundedCorners.mContext = mContext;
- mRoundedCorners.mComponents = mContext.getComponents();
+ mScreenDecorations = new ScreenDecorations();
+ mScreenDecorations.mContext = mContext;
+ mScreenDecorations.mComponents = mContext.getComponents();
mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class);
}
@Test
- public void testNoRounding() {
+ public void testNoRounding_NoCutout() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
- mRoundedCorners.start();
+ mScreenDecorations.start();
// No views added.
verify(mWindowManager, never()).addView(any(), any());
// No Fragments watched.
@@ -105,11 +112,13 @@
@Test
public void testRounding() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 20);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 20);
- mRoundedCorners.start();
+ mScreenDecorations.start();
// Add 2 windows for rounded corners (top and bottom).
verify(mWindowManager, times(2)).addView(any(), any());
@@ -122,6 +131,44 @@
}
@Test
+ public void testCutout() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, true);
+ mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(dimen.rounded_corner_content_padding, 0);
+
+ mScreenDecorations.start();
+ // Add 2 windows for rounded corners (top and bottom).
+ verify(mWindowManager, times(2)).addView(any(), any());
+ }
+
+ @Test
+ public void testDelayedCutout() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+ mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(dimen.rounded_corner_content_padding, 0);
+
+ mScreenDecorations.start();
+
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, true);
+ mScreenDecorations.onConfigurationChanged(new Configuration());
+
+ // Add 2 windows for rounded corners (top and bottom).
+ verify(mWindowManager, times(2)).addView(any(), any());
+ }
+
+ @Test
+ public void hasRoundedCornerOverlayFlagSet() {
+ assertThat(mScreenDecorations.getWindowLayoutParams().privateFlags
+ & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
+ is(PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY));
+ }
+
+ @Test
public void testPaddingTagListener() {
TunablePaddingTagListener tagListener = new TunablePaddingTagListener(14, 5);
View v = mock(View.class);
@@ -136,7 +183,7 @@
// Trigger callback and verify we get a TunablePadding created.
tagListener.onFragmentViewCreated(null, f);
- verify(mTunablePaddingService).add(eq(child), eq(RoundedCorners.PADDING), eq(14),
+ verify(mTunablePaddingService).add(eq(child), eq(ScreenDecorations.PADDING), eq(14),
eq(FLAG_START | FLAG_END));
// Call again and verify destroy is called.