From f1fc5d21b1379bf8890d9f5fd92cfb44e6f46b9e Mon Sep 17 00:00:00 2001 From: Milton Wu Date: Tue, 21 Jun 2022 10:05:18 +0000 Subject: [PATCH 1/2] Move color and display info into DecorProvider Original tint color and display info are handled inside ScreenDecorations like specific cases. Move these info into interface of DecorProvider. Bug: 232874879 Test: atest ScreenDecorationsTest ScreenDecorHwcLayerTest \ OverlayWindowTest RoundedCornerResDelegateTest Test: fold/unfold/rotate/debugRoundedCornerSize/debugFlag on sw-layer devices Test: rotate/debugRoundedCornerSize/debugFlag/resolutionChange/faceAnim on hw-layer devices Change-Id: I1fdcbe4c5e8ee0a54eba03f59b9066e76e5c9a03 Merged-In: I1fdcbe4c5e8ee0a54eba03f59b9066e76e5c9a03 (cherry picked from commit 3353759edfb1915ebaf36318cddf6db20505c461) --- .../android/systemui/DisplayCutoutBaseView.kt | 53 ++++++---- .../android/systemui/ScreenDecorations.java | 76 +++++--------- .../android/systemui/decor/DecorProvider.kt | 8 +- .../decor/FaceScanningProviderFactory.kt | 11 ++- .../android/systemui/decor/OverlayWindow.kt | 30 +++--- .../decor/PrivacyDotDecorProviderFactory.kt | 6 +- .../decor/RoundedCornerDecorProviderImpl.kt | 37 ++++--- .../decor/RoundedCornerResDelegate.kt | 37 ++++--- .../systemui/ScreenDecorationsTest.java | 4 +- .../systemui/decor/OverlayWindowTest.kt | 99 +++++++++++-------- 10 files changed, 197 insertions(+), 164 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt index d2c229b8ead11..a3351e1a64400 100644 --- a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt +++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt @@ -47,7 +47,8 @@ import com.android.systemui.animation.Interpolators open class DisplayCutoutBaseView : View, RegionInterceptableView { private var shouldDrawCutout: Boolean = DisplayCutout.getFillBuiltInDisplayCutout( - context.resources, context.display?.uniqueId) + context.resources, context.display?.uniqueId + ) private var displayUniqueId: String? = null private var displayMode: Display.Mode? = null protected val location = IntArray(2) @@ -74,8 +75,8 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) - : super(context, attrs, defStyleAttr) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : + super(context, attrs, defStyleAttr) override fun onAttachedToWindow() { super.onAttachedToWindow() @@ -85,7 +86,7 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { onUpdate() } - fun onDisplayChanged(displayId: Int) { + fun onDisplayChanged(newDisplayUniqueId: String?) { val oldMode: Display.Mode? = displayMode val display: Display? = context.display displayMode = display?.mode @@ -93,7 +94,8 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { if (displayUniqueId != display?.uniqueId) { displayUniqueId = display?.uniqueId shouldDrawCutout = DisplayCutout.getFillBuiltInDisplayCutout( - context.resources, displayUniqueId) + context.resources, displayUniqueId + ) } // Skip if display mode or cutout hasn't changed. @@ -101,7 +103,7 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { display?.cutout == displayInfo.displayCutout) { return } - if (displayId == display?.displayId) { + if (newDisplayUniqueId == display?.uniqueId) { updateCutout() updateProtectionBoundingPath() onUpdate() @@ -147,8 +149,9 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { cutoutBounds.translate(-location[0], -location[1]) // Intersect with window's frame - cutoutBounds.op(rootView.left, rootView.top, rootView.right, rootView.bottom, - Region.Op.INTERSECT) + cutoutBounds.op( + rootView.left, rootView.top, rootView.right, rootView.bottom, Region.Op.INTERSECT + ) return cutoutBounds } @@ -171,9 +174,12 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { protected open fun drawCutoutProtection(canvas: Canvas) { if (cameraProtectionProgress > HIDDEN_CAMERA_PROTECTION_SCALE && - !protectionRect.isEmpty) { - canvas.scale(cameraProtectionProgress, cameraProtectionProgress, - protectionRect.centerX(), protectionRect.centerY()) + !protectionRect.isEmpty + ) { + canvas.scale( + cameraProtectionProgress, cameraProtectionProgress, protectionRect.centerX(), + protectionRect.centerY() + ) canvas.drawPath(protectionPath, paint) } } @@ -205,14 +211,17 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { requestLayout() } cameraProtectionAnimator?.cancel() - cameraProtectionAnimator = ValueAnimator.ofFloat(cameraProtectionProgress, - if (showProtection) 1.0f else HIDDEN_CAMERA_PROTECTION_SCALE).setDuration(750) + cameraProtectionAnimator = ValueAnimator.ofFloat( + cameraProtectionProgress, + if (showProtection) 1.0f else HIDDEN_CAMERA_PROTECTION_SCALE + ).setDuration(750) cameraProtectionAnimator?.interpolator = Interpolators.DECELERATE_QUINT - cameraProtectionAnimator?.addUpdateListener(ValueAnimator.AnimatorUpdateListener { - animation: ValueAnimator -> - cameraProtectionProgress = animation.animatedValue as Float - invalidate() - }) + cameraProtectionAnimator?.addUpdateListener( + ValueAnimator.AnimatorUpdateListener { animation: ValueAnimator -> + cameraProtectionProgress = animation.animatedValue as Float + invalidate() + } + ) cameraProtectionAnimator?.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { cameraProtectionAnimator = null @@ -245,8 +254,10 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { // Apply rotation. val lw: Int = displayInfo.logicalWidth val lh: Int = displayInfo.logicalHeight - val flipped = (displayInfo.rotation == Surface.ROTATION_90 || - displayInfo.rotation == Surface.ROTATION_270) + val flipped = ( + displayInfo.rotation == Surface.ROTATION_90 || + displayInfo.rotation == Surface.ROTATION_270 + ) val dw = if (flipped) lh else lw val dh = if (flipped) lw else lh transformPhysicalToLogicalCoordinates(displayInfo.rotation, dw, dh, m) @@ -275,7 +286,7 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { // We purposely ignore refresh rate and id changes here, because we don't need to // invalidate for those, and they can trigger the refresh rate to increase return oldMode?.physicalHeight != newMode?.physicalHeight || - oldMode?.physicalWidth != newMode?.physicalWidth + oldMode?.physicalWidth != newMode?.physicalWidth } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index edcaf492874da..4c400a81024f5 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -32,7 +32,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; -import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Color; @@ -92,10 +91,8 @@ import com.android.systemui.util.settings.SecureSettings; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Objects; -import java.util.Set; import java.util.concurrent.Executor; import javax.inject.Inject; @@ -448,6 +445,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } } + boolean needToUpdateProviderViews = false; final String newUniqueId = mDisplayInfo.uniqueId; if (!Objects.equals(newUniqueId, mDisplayUniqueId)) { mDisplayUniqueId = newUniqueId; @@ -470,8 +468,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab updateHwLayerRoundedCornerDrawable(); updateHwLayerRoundedCornerExistAndSize(); } - - updateOverlayProviderViews(); + needToUpdateProviderViews = true; } final float newRatio = getPhysicalPixelDisplaySizeRatio(); @@ -480,7 +477,13 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab if (mScreenDecorHwcLayer != null) { updateHwLayerRoundedCornerExistAndSize(); } - updateOverlayProviderViews(); + needToUpdateProviderViews = true; + } + + if (needToUpdateProviderViews) { + updateOverlayProviderViews(null); + } else { + updateOverlayProviderViews(new Integer[] { mFaceScanningViewId }); } if (mCutoutViews != null) { @@ -490,18 +493,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab if (cutoutView == null) { continue; } - cutoutView.onDisplayChanged(displayId); + cutoutView.onDisplayChanged(newUniqueId); } } - DisplayCutoutView overlay = (DisplayCutoutView) getOverlayView(mFaceScanningViewId); - if (overlay != null) { - // handle display resolution changes - overlay.onDisplayChanged(displayId); - } - if (mScreenDecorHwcLayer != null) { - mScreenDecorHwcLayer.onDisplayChanged(displayId); + mScreenDecorHwcLayer.onDisplayChanged(newUniqueId); } } }; @@ -804,7 +801,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return; } removeOverlayView(provider.getViewId()); - overlay.addDecorProvider(provider, mRotation); + overlay.addDecorProvider(provider, mRotation, mTintColor); }); } // Use visibility of privacy dot views & face scanning view to determine the overlay's @@ -955,24 +952,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return; } - // When the hwc supports screen decorations, the layer will use the A8 color mode which - // won't be affected by the color inversion. If the composition goes the client composition - // route, the color inversion will be handled by the RenderEngine. - final Set viewsMayNeedColorUpdate = new HashSet<>(); - if (mHwcScreenDecorationSupport == null) { - ColorStateList tintList = ColorStateList.valueOf(mTintColor); - mRoundedCornerResDelegate.setColorTintList(tintList); - viewsMayNeedColorUpdate.add(R.id.rounded_corner_top_left); - viewsMayNeedColorUpdate.add(R.id.rounded_corner_top_right); - viewsMayNeedColorUpdate.add(R.id.rounded_corner_bottom_left); - viewsMayNeedColorUpdate.add(R.id.rounded_corner_bottom_right); - viewsMayNeedColorUpdate.add(R.id.display_cutout); - } - if (getOverlayView(mFaceScanningViewId) != null) { - viewsMayNeedColorUpdate.add(mFaceScanningViewId); - } - final Integer[] views = new Integer[viewsMayNeedColorUpdate.size()]; - viewsMayNeedColorUpdate.toArray(views); for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { if (mOverlays[i] == null) { continue; @@ -982,14 +961,19 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab View child; for (int j = 0; j < size; j++) { child = overlayView.getChildAt(j); - if (viewsMayNeedColorUpdate.contains(child.getId()) - && child instanceof DisplayCutoutView) { + if (child instanceof DisplayCutoutView && child.getId() == R.id.display_cutout) { ((DisplayCutoutView) child).setColor(mTintColor); } } - mOverlays[i].onReloadResAndMeasure(views, mProviderRefreshToken, - mRotation, mDisplayUniqueId); } + + updateOverlayProviderViews(new Integer[] { + mFaceScanningViewId, + R.id.rounded_corner_top_left, + R.id.rounded_corner_top_right, + R.id.rounded_corner_bottom_left, + R.id.rounded_corner_bottom_right + }); } @VisibleForTesting @@ -1120,7 +1104,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } // update all provider views inside overlay - updateOverlayProviderViews(); + updateOverlayProviderViews(null); } FaceScanningOverlay faceScanningOverlay = @@ -1192,7 +1176,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab context.getResources(), context.getDisplay().getUniqueId()); } - private void updateOverlayProviderViews() { + private void updateOverlayProviderViews(@Nullable Integer[] filterIds) { if (mOverlays == null) { return; } @@ -1201,7 +1185,8 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab if (overlay == null) { continue; } - overlay.onReloadResAndMeasure(null, mProviderRefreshToken, mRotation, mDisplayUniqueId); + overlay.onReloadResAndMeasure(filterIds, mProviderRefreshToken, mRotation, mTintColor, + mDisplayUniqueId); } } @@ -1240,19 +1225,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } catch (NumberFormatException e) { mRoundedCornerResDelegate.setTuningSizeFactor(null); } - Integer[] filterIds = { + updateOverlayProviderViews(new Integer[] { R.id.rounded_corner_top_left, R.id.rounded_corner_top_right, R.id.rounded_corner_bottom_left, R.id.rounded_corner_bottom_right - }; - for (final OverlayWindow overlay: mOverlays) { - if (overlay == null) { - continue; - } - overlay.onReloadResAndMeasure(filterIds, mProviderRefreshToken, mRotation, - mDisplayUniqueId); - } + }); updateHwLayerRoundedCornerExistAndSize(); }); } diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt index 169b50edccfc8..de6d7278971c5 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt @@ -33,7 +33,7 @@ abstract class DecorProvider { /** The number of total aligned bounds */ val numOfAlignedEdge: Int - get() = alignedBounds.size + get() = alignedBounds.size /** The aligned bounds for the view which is created through inflateView() */ abstract val alignedBounds: List @@ -46,14 +46,16 @@ abstract class DecorProvider { view: View, reloadToken: Int, @Surface.Rotation rotation: Int, - displayUniqueId: String? = null + tintColor: Int, + displayUniqueId: String? ) /** Inflate view into parent as current rotation */ abstract fun inflateView( context: Context, parent: ViewGroup, - @Surface.Rotation rotation: Int + @Surface.Rotation rotation: Int, + tintColor: Int ): View } diff --git a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt index 81d3d6caebd79..5925c57b45185 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt @@ -115,19 +115,25 @@ class FaceScanningOverlayProviderImpl( override fun onReloadResAndMeasure( view: View, reloadToken: Int, - rotation: Int, + @Surface.Rotation rotation: Int, + tintColor: Int, displayUniqueId: String? ) { (view.layoutParams as FrameLayout.LayoutParams).let { updateLayoutParams(it, rotation) view.layoutParams = it + (view as? FaceScanningOverlay)?.let { overlay -> + overlay.setColor(tintColor) + overlay.onDisplayChanged(displayUniqueId) + } } } override fun inflateView( context: Context, parent: ViewGroup, - @Surface.Rotation rotation: Int + @Surface.Rotation rotation: Int, + tintColor: Int ): View { val view = FaceScanningOverlay( context, @@ -137,6 +143,7 @@ class FaceScanningOverlayProviderImpl( mainExecutor ) view.id = viewId + view.setColor(tintColor) FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT).let { updateLayoutParams(it, rotation) diff --git a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt index 3c0748e025522..dfb0b5aad9124 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt @@ -34,9 +34,10 @@ class OverlayWindow(private val context: Context) { fun addDecorProvider( decorProvider: DecorProvider, - @Surface.Rotation rotation: Int + @Surface.Rotation rotation: Int, + tintColor: Int ) { - val view = decorProvider.inflateView(context, rootView, rotation) + val view = decorProvider.inflateView(context, rootView, rotation, tintColor) viewProviderMap[decorProvider.viewId] = Pair(view, decorProvider) } @@ -69,7 +70,7 @@ class OverlayWindow(private val context: Context) { */ fun hasSameProviders(newProviders: List): Boolean { return (newProviders.size == viewProviderMap.size) && - newProviders.all { getView(it.viewId) != null } + newProviders.all { getView(it.viewId) != null } } /** @@ -82,23 +83,28 @@ class OverlayWindow(private val context: Context) { filterIds: Array? = null, reloadToken: Int, @Surface.Rotation rotation: Int, + tintColor: Int, displayUniqueId: String? = null ) { filterIds?.forEach { id -> viewProviderMap[id]?.let { it.second.onReloadResAndMeasure( - view = it.first, - reloadToken = reloadToken, - displayUniqueId = displayUniqueId, - rotation = rotation) + view = it.first, + reloadToken = reloadToken, + rotation = rotation, + tintColor = tintColor, + displayUniqueId = displayUniqueId + ) } } ?: run { viewProviderMap.values.forEach { it.second.onReloadResAndMeasure( - view = it.first, - reloadToken = reloadToken, - displayUniqueId = displayUniqueId, - rotation = rotation) + view = it.first, + reloadToken = reloadToken, + rotation = rotation, + tintColor = tintColor, + displayUniqueId = displayUniqueId + ) } } } @@ -111,4 +117,4 @@ class OverlayWindow(private val context: Context) { pw.println(" child[$i]=$child") } } -} \ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt index 9f624b3dcb094..e18c0e15b57a7 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt @@ -20,9 +20,9 @@ import android.content.Context import android.content.res.Resources import android.view.DisplayCutout import android.view.LayoutInflater +import android.view.Surface import android.view.View import android.view.ViewGroup -import android.view.Surface import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main @@ -85,6 +85,7 @@ class PrivacyDotCornerDecorProviderImpl( view: View, reloadToken: Int, rotation: Int, + tintColor: Int, displayUniqueId: String? ) { // Do nothing here because it is handled inside PrivacyDotViewController @@ -93,7 +94,8 @@ class PrivacyDotCornerDecorProviderImpl( override fun inflateView( context: Context, parent: ViewGroup, - @Surface.Rotation rotation: Int + @Surface.Rotation rotation: Int, + tintColor: Int ): View { LayoutInflater.from(context).inflate(layoutId, parent, true) return parent.getChildAt(parent.childCount - 1 /* latest new added child */) diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt index e316722b64ea6..8156797c5a677 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt @@ -17,6 +17,7 @@ package com.android.systemui.decor import android.content.Context +import android.content.res.ColorStateList import android.view.DisplayCutout import android.view.Gravity import android.view.Surface @@ -38,12 +39,13 @@ class RoundedCornerDecorProviderImpl( override fun inflateView( context: Context, parent: ViewGroup, - @Surface.Rotation rotation: Int + @Surface.Rotation rotation: Int, + tintColor: Int ): View { return ImageView(context).also { view -> // View view.id = viewId - initView(view, rotation) + initView(view, rotation, tintColor) // LayoutParams val layoutSize = if (isTop) { @@ -52,31 +54,36 @@ class RoundedCornerDecorProviderImpl( roundedCornerResDelegate.bottomRoundedSize } val params = FrameLayout.LayoutParams( - layoutSize.width, - layoutSize.height, - alignedBound1.toLayoutGravity(rotation) or - alignedBound2.toLayoutGravity(rotation)) + layoutSize.width, + layoutSize.height, + alignedBound1.toLayoutGravity(rotation) or alignedBound2.toLayoutGravity(rotation) + ) // AddView parent.addView(view, params) } } - private fun initView(view: ImageView, @Surface.Rotation rotation: Int) { + private fun initView( + view: ImageView, + @Surface.Rotation rotation: Int, + tintColor: Int + ) { view.setRoundedCornerImage(roundedCornerResDelegate, isTop) view.adjustRotation(alignedBounds, rotation) - view.imageTintList = roundedCornerResDelegate.colorTintList + view.imageTintList = ColorStateList.valueOf(tintColor) } override fun onReloadResAndMeasure( view: View, reloadToken: Int, @Surface.Rotation rotation: Int, + tintColor: Int, displayUniqueId: String? ) { roundedCornerResDelegate.updateDisplayUniqueId(displayUniqueId, reloadToken) - initView((view as ImageView), rotation) + initView((view as ImageView), rotation, tintColor) val layoutSize = if (isTop) { roundedCornerResDelegate.topRoundedSize @@ -87,7 +94,7 @@ class RoundedCornerDecorProviderImpl( it.width = layoutSize.width it.height = layoutSize.height it.gravity = alignedBound1.toLayoutGravity(rotation) or - alignedBound2.toLayoutGravity(rotation) + alignedBound2.toLayoutGravity(rotation) view.setLayoutParams(it) } } @@ -134,10 +141,10 @@ private fun ImageView.setRoundedCornerImage( setImageDrawable(drawable) } else { setImageResource( - if (isTop) - R.drawable.rounded_corner_top - else - R.drawable.rounded_corner_bottom + if (isTop) + R.drawable.rounded_corner_top + else + R.drawable.rounded_corner_bottom ) } } @@ -187,4 +194,4 @@ private fun ImageView.adjustRotation(alignedBounds: List, @Surface.Rotation this.rotation = newRotation this.scaleX = newScaleX this.scaleY = newScaleY -} \ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt index b5a0cfcb1c184..a252864383876 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt @@ -18,9 +18,7 @@ package com.android.systemui.decor import android.annotation.ArrayRes import android.annotation.DrawableRes -import android.content.res.ColorStateList import android.content.res.Resources -import android.graphics.Color import android.graphics.drawable.Drawable import android.util.DisplayUtils import android.util.Size @@ -57,8 +55,6 @@ class RoundedCornerResDelegate( var bottomRoundedSize = Size(0, 0) private set - var colorTintList = ColorStateList.valueOf(Color.BLACK) - var tuningSizeFactor: Int? = null set(value) { if (field == value) { @@ -107,19 +103,19 @@ class RoundedCornerResDelegate( val hasDefaultRadius = RoundedCorners.getRoundedCornerRadius(res, displayUniqueId) > 0 hasTop = hasDefaultRadius || - (RoundedCorners.getRoundedCornerTopRadius(res, displayUniqueId) > 0) + (RoundedCorners.getRoundedCornerTopRadius(res, displayUniqueId) > 0) hasBottom = hasDefaultRadius || - (RoundedCorners.getRoundedCornerBottomRadius(res, displayUniqueId) > 0) + (RoundedCorners.getRoundedCornerBottomRadius(res, displayUniqueId) > 0) topRoundedDrawable = getDrawable( - displayConfigIndex = configIdx, - arrayResId = R.array.config_roundedCornerTopDrawableArray, - backupDrawableId = R.drawable.rounded_corner_top + displayConfigIndex = configIdx, + arrayResId = R.array.config_roundedCornerTopDrawableArray, + backupDrawableId = R.drawable.rounded_corner_top ) bottomRoundedDrawable = getDrawable( - displayConfigIndex = configIdx, - arrayResId = R.array.config_roundedCornerBottomDrawableArray, - backupDrawableId = R.drawable.rounded_corner_bottom + displayConfigIndex = configIdx, + arrayResId = R.array.config_roundedCornerBottomDrawableArray, + backupDrawableId = R.drawable.rounded_corner_bottom ) } @@ -147,13 +143,15 @@ class RoundedCornerResDelegate( if (physicalPixelDisplaySizeRatio != 1f) { if (topRoundedSize.width != 0) { topRoundedSize = Size( - (physicalPixelDisplaySizeRatio * topRoundedSize.width + 0.5f).toInt(), - (physicalPixelDisplaySizeRatio * topRoundedSize.height + 0.5f).toInt()) + (physicalPixelDisplaySizeRatio * topRoundedSize.width + 0.5f).toInt(), + (physicalPixelDisplaySizeRatio * topRoundedSize.height + 0.5f).toInt() + ) } if (bottomRoundedSize.width != 0) { bottomRoundedSize = Size( - (physicalPixelDisplaySizeRatio * bottomRoundedSize.width + 0.5f).toInt(), - (physicalPixelDisplaySizeRatio * bottomRoundedSize.height + 0.5f).toInt()) + (physicalPixelDisplaySizeRatio * bottomRoundedSize.width + 0.5f).toInt(), + (physicalPixelDisplaySizeRatio * bottomRoundedSize.height + 0.5f).toInt() + ) } } } @@ -180,8 +178,9 @@ class RoundedCornerResDelegate( pw.println(" hasTop=$hasTop") pw.println(" hasBottom=$hasBottom") pw.println(" topRoundedSize(w,h)=(${topRoundedSize.width},${topRoundedSize.height})") - pw.println(" bottomRoundedSize(w,h)=(${bottomRoundedSize.width}," + - "${bottomRoundedSize.height})") + pw.println( + " bottomRoundedSize(w,h)=(${bottomRoundedSize.width},${bottomRoundedSize.height})" + ) pw.println(" physicalPixelDisplaySizeRatio=$physicalPixelDisplaySizeRatio") } -} \ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index 90609fa2772f4..64a7986d05b1f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -1383,7 +1383,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.mDisplayListener.onDisplayChanged(1); - verify(hwcLayer, times(1)).onDisplayChanged(1); + verify(hwcLayer, times(1)).onDisplayChanged(any()); } @Test @@ -1407,7 +1407,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.mDisplayListener.onDisplayChanged(1); - verify(cutoutView, times(1)).onDisplayChanged(1); + verify(cutoutView, times(1)).onDisplayChanged(any()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt index 69366fa0d4a94..8bf17d7c62f99 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.decor +import android.graphics.Color import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.DisplayCutout @@ -51,35 +52,45 @@ class OverlayWindowTest : SysuiTestCase() { @Before fun setUp() { - decorProvider1 = spy(PrivacyDotCornerDecorProviderImpl( + decorProvider1 = spy( + PrivacyDotCornerDecorProviderImpl( viewId = TEST_DECOR_VIEW_ID_1, alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, - layoutId = R.layout.privacy_dot_top_left)) - decorProvider2 = spy(PrivacyDotCornerDecorProviderImpl( + layoutId = R.layout.privacy_dot_top_left + ) + ) + decorProvider2 = spy( + PrivacyDotCornerDecorProviderImpl( viewId = TEST_DECOR_VIEW_ID_2, alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, - layoutId = R.layout.privacy_dot_bottom_left)) - decorProvider3 = spy(PrivacyDotCornerDecorProviderImpl( + layoutId = R.layout.privacy_dot_bottom_left + ) + ) + decorProvider3 = spy( + PrivacyDotCornerDecorProviderImpl( viewId = TEST_DECOR_VIEW_ID_3, alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, - layoutId = R.layout.privacy_dot_bottom_right)) - + layoutId = R.layout.privacy_dot_bottom_right + ) + ) overlay = OverlayWindow(mContext) } @Test fun testAddProvider() { @Surface.Rotation val rotation = Surface.ROTATION_270 - overlay.addDecorProvider(decorProvider1, rotation) - overlay.addDecorProvider(decorProvider2, rotation) + overlay.addDecorProvider(decorProvider1, rotation, Color.BLACK) + overlay.addDecorProvider(decorProvider2, rotation, Color.YELLOW) verify(decorProvider1, times(1)).inflateView( - mContext, overlay.rootView, rotation) + mContext, overlay.rootView, rotation, Color.BLACK + ) verify(decorProvider2, times(1)).inflateView( - mContext, overlay.rootView, rotation) + mContext, overlay.rootView, rotation, Color.YELLOW + ) val view1FoundFromRootView = overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_1) Assert.assertNotNull(view1FoundFromRootView) @@ -91,8 +102,8 @@ class OverlayWindowTest : SysuiTestCase() { @Test fun testRemoveView() { - overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270) - overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270) + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270, Color.BLACK) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270, Color.BLACK) overlay.removeView(TEST_DECOR_VIEW_ID_1) val viewFoundFromRootView = overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_1) @@ -102,39 +113,47 @@ class OverlayWindowTest : SysuiTestCase() { @Test fun testOnReloadResAndMeasureWithoutIds() { - overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) - overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0, Color.BLACK) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0, Color.BLACK) overlay.onReloadResAndMeasure( - reloadToken = 1, - rotation = Surface.ROTATION_90, - displayUniqueId = null) + reloadToken = 1, + rotation = Surface.ROTATION_90, + tintColor = Color.BLACK, + displayUniqueId = null + ) verify(decorProvider1, times(1)).onReloadResAndMeasure( - overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null) + overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, Color.BLACK, null + ) verify(decorProvider2, times(1)).onReloadResAndMeasure( - overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null) + overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, Color.BLACK, null + ) } @Test fun testOnReloadResAndMeasureWithIds() { - overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) - overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0, Color.BLACK) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0, Color.BLACK) overlay.onReloadResAndMeasure( - filterIds = arrayOf(TEST_DECOR_VIEW_ID_2), - reloadToken = 1, - rotation = Surface.ROTATION_90, - displayUniqueId = null) + filterIds = arrayOf(TEST_DECOR_VIEW_ID_2), + reloadToken = 1, + rotation = Surface.ROTATION_90, + tintColor = Color.BLACK, + displayUniqueId = null + ) verify(decorProvider1, never()).onReloadResAndMeasure( - overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null) + overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, Color.BLACK, null + ) verify(decorProvider2, times(1)).onReloadResAndMeasure( - overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null) + overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, Color.BLACK, null + ) } @Test fun testRemoveRedundantViewsWithNullParameter() { - overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270) - overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270) + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270, Color.BLACK) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270, Color.BLACK) overlay.removeRedundantViews(null) @@ -146,13 +165,15 @@ class OverlayWindowTest : SysuiTestCase() { @Test fun testRemoveRedundantViewsWith2Providers() { - overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270) - overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270) + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270, Color.BLACK) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270, Color.BLACK) - overlay.removeRedundantViews(IntArray(2).apply { - this[0] = TEST_DECOR_VIEW_ID_3 - this[1] = TEST_DECOR_VIEW_ID_1 - }) + overlay.removeRedundantViews( + IntArray(2).apply { + this[0] = TEST_DECOR_VIEW_ID_3 + this[1] = TEST_DECOR_VIEW_ID_1 + } + ) Assert.assertNotNull(overlay.getView(TEST_DECOR_VIEW_ID_1)) Assert.assertNotNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_1)) @@ -167,16 +188,16 @@ class OverlayWindowTest : SysuiTestCase() { Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2))) Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1))) - overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0, Color.BLACK) Assert.assertFalse(overlay.hasSameProviders(emptyList())) Assert.assertTrue(overlay.hasSameProviders(listOf(decorProvider1))) Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2))) Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1))) - overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0, Color.BLACK) Assert.assertFalse(overlay.hasSameProviders(emptyList())) Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider1))) Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2))) Assert.assertTrue(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1))) } -} \ No newline at end of file +} From 6e4eccabbe12e066d035767a131cf38730fb159b Mon Sep 17 00:00:00 2001 From: Milton Wu Date: Wed, 3 Aug 2022 05:44:06 +0000 Subject: [PATCH 2/2] Re-land ScreenDecorations Cutout Provider Move cutout to provider. Bug: 232874879 Test: atest ScreenDecorationsTest CutoutDecorProviderFactoryTest \ OverlayWindowTest Test: fold/unfold/rotate/debugRoundedCornerSize/debugFlag on sw-layer devices Test: adb shell dumpsys activity service com.android.systemui Change-Id: I5f0b1e08ba7fee503b0926001cbdbd752edf2d48 --- packages/SystemUI/res/values/ids.xml | 4 + .../android/systemui/ScreenDecorations.java | 200 +++++-------- .../decor/CutoutDecorProviderFactory.kt | 60 ++++ .../systemui/decor/CutoutDecorProviderImpl.kt | 65 ++++ .../android/systemui/decor/DecorProvider.kt | 70 ++++- .../android/systemui/decor/OverlayWindow.kt | 3 +- .../systemui/ScreenDecorationsTest.java | 282 ++++++------------ .../decor/CutoutDecorProviderFactoryTest.kt | 200 +++++++++++++ 8 files changed, 557 insertions(+), 327 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderFactory.kt create mode 100644 packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index dca5ea836fa47..f88f46f2a3c82 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -131,7 +131,11 @@ + + + + diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 4c400a81024f5..2e13903814a5e 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -72,6 +72,7 @@ import com.android.settingslib.Utils; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.decor.CutoutDecorProviderFactory; import com.android.systemui.decor.DecorProvider; import com.android.systemui.decor.DecorProviderFactory; import com.android.systemui.decor.DecorProviderKt; @@ -118,6 +119,13 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab private static final boolean VERBOSE = false; static final boolean DEBUG_COLOR = DEBUG_SCREENSHOT_ROUNDED_CORNERS; + private static final int[] DISPLAY_CUTOUT_IDS = { + R.id.display_cutout, + R.id.display_cutout_left, + R.id.display_cutout_right, + R.id.display_cutout_bottom + }; + private DisplayManager mDisplayManager; @VisibleForTesting protected boolean mIsRegistered; @@ -139,13 +147,11 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab protected RoundedCornerResDelegate mRoundedCornerResDelegate; @VisibleForTesting protected DecorProviderFactory mRoundedCornerFactory; + private CutoutDecorProviderFactory mCutoutFactory; private int mProviderRefreshToken = 0; @VisibleForTesting protected OverlayWindow[] mOverlays = null; @VisibleForTesting - @Nullable - DisplayCutoutView[] mCutoutViews; - @VisibleForTesting ViewGroup mScreenDecorHwcWindow; @VisibleForTesting ScreenDecorHwcLayer mScreenDecorHwcLayer; @@ -187,18 +193,19 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return; } - if (mCutoutViews == null) { - Log.w(TAG, "DisplayCutoutView not initialized onApplyCameraProtection"); - return; - } - - // Show the extra protection around the front facing camera if necessary - for (DisplayCutoutView dcv : mCutoutViews) { - // Check Null since not all mCutoutViews[pos] be inflated at the meanwhile - if (dcv != null) { - dcv.setProtection(protectionPath, bounds); - dcv.enableShowProtection(true); + int setProtectionCnt = 0; + for (int id: DISPLAY_CUTOUT_IDS) { + final View view = getOverlayView(id); + if (!(view instanceof DisplayCutoutView)) { + continue; } + ++setProtectionCnt; + final DisplayCutoutView dcv = (DisplayCutoutView) view; + dcv.setProtection(protectionPath, bounds); + dcv.enableShowProtection(true); + } + if (setProtectionCnt == 0) { + Log.e(TAG, "CutoutView not initialized showCameraProtection"); } } @@ -219,16 +226,17 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return; } - if (mCutoutViews == null) { - Log.w(TAG, "DisplayCutoutView not initialized onHideCameraProtection"); - return; - } - // Go back to the regular anti-aliasing - for (DisplayCutoutView dcv : mCutoutViews) { - // Check Null since not all mCutoutViews[pos] be inflated at the meanwhile - if (dcv != null) { - dcv.enableShowProtection(false); + int setProtectionCnt = 0; + for (int id: DISPLAY_CUTOUT_IDS) { + final View view = getOverlayView(id); + if (!(view instanceof DisplayCutoutView)) { + continue; } + ++setProtectionCnt; + ((DisplayCutoutView) view).enableShowProtection(false); + } + if (setProtectionCnt == 0) { + Log.e(TAG, "CutoutView not initialized hideCameraProtection"); } } @@ -335,6 +343,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab decorProviders.addAll(mFaceScanningFactory.getProviders()); if (!hasHwLayer) { decorProviders.addAll(mRoundedCornerFactory.getProviders()); + decorProviders.addAll(mCutoutFactory.getProviders()); } return decorProviders; } @@ -379,6 +388,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio( getPhysicalPixelDisplaySizeRatio()); mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate); + mCutoutFactory = getCutoutFactory(); mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); updateHwLayerRoundedCornerDrawable(); setupDecorations(); @@ -483,18 +493,13 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab if (needToUpdateProviderViews) { updateOverlayProviderViews(null); } else { - updateOverlayProviderViews(new Integer[] { mFaceScanningViewId }); - } - - if (mCutoutViews != null) { - final int size = mCutoutViews.length; - for (int i = 0; i < size; i++) { - final DisplayCutoutView cutoutView = mCutoutViews[i]; - if (cutoutView == null) { - continue; - } - cutoutView.onDisplayChanged(newUniqueId); - } + updateOverlayProviderViews(new Integer[] { + mFaceScanningViewId, + R.id.display_cutout, + R.id.display_cutout_left, + R.id.display_cutout_right, + R.id.display_cutout_bottom, + }); } if (mScreenDecorHwcLayer != null) { @@ -507,8 +512,9 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab updateConfiguration(); } + @VisibleForTesting @Nullable - private View getOverlayView(@IdRes int id) { + View getOverlayView(@IdRes int id) { if (mOverlays == null) { return null; } @@ -565,18 +571,18 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab removeHwcOverlay(); } - final DisplayCutout cutout = getCutout(); + boolean[] hasCreatedOverlay = new boolean[BOUNDS_POSITION_LENGTH]; final boolean shouldOptimizeVisibility = shouldOptimizeVisibility(); + Integer bound; + while ((bound = DecorProviderKt.getProperBound(decorProviders)) != null) { + hasCreatedOverlay[bound] = true; + Pair, List> pair = + DecorProviderKt.partitionAlignedBound(decorProviders, bound); + decorProviders = pair.getSecond(); + createOverlay(bound, pair.getFirst(), shouldOptimizeVisibility); + } for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (shouldShowSwLayerCutout(i, cutout) - || shouldShowSwLayerFaceScan(i, cutout) - || shouldShowSwLayerRoundedCorner(i, cutout) - || shouldShowSwLayerPrivacyDot(i, cutout)) { - Pair, List> pair = - DecorProviderKt.partitionAlignedBound(decorProviders, i); - decorProviders = pair.getSecond(); - createOverlay(i, pair.getFirst(), shouldOptimizeVisibility); - } else { + if (!hasCreatedOverlay[i]) { removeOverlay(i); } } @@ -639,9 +645,10 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } } - @VisibleForTesting - DisplayCutout getCutout() { - return mContext.getDisplay().getCutout(); + // For unit test to override + protected CutoutDecorProviderFactory getCutoutFactory() { + return new CutoutDecorProviderFactory(mContext.getResources(), + mContext.getDisplay()); } @VisibleForTesting @@ -731,16 +738,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab overlayView.setAlpha(0); overlayView.setForceDarkAllowed(false); - // Only show cutout in mOverlays when hwc doesn't support screen decoration - if (mHwcScreenDecorationSupport == null) { - if (mCutoutViews == null) { - mCutoutViews = new DisplayCutoutView[BOUNDS_POSITION_LENGTH]; - } - mCutoutViews[pos] = new DisplayCutoutView(mContext, pos); - overlayView.addView(mCutoutViews[pos]); - mCutoutViews[pos].updateRotation(mRotation); - } - mWindowManager.addView(overlayView, getWindowLayoutParams(pos)); overlayView.addOnLayoutChangeListener(new OnLayoutChangeListener() { @@ -920,6 +917,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } private void setupCameraListener() { + // TODO(b/238143614) Support dual screen camera protection Resources res = mContext.getResources(); boolean enabled = res.getBoolean(R.bool.config_enableDisplayCutoutProtection); if (enabled) { @@ -948,27 +946,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mTintColor = Color.RED; } - if (mOverlays == null) { - return; - } - - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { - continue; - } - final ViewGroup overlayView = mOverlays[i].getRootView(); - final int size = overlayView.getChildCount(); - View child; - for (int j = 0; j < size; j++) { - child = overlayView.getChildAt(j); - if (child instanceof DisplayCutoutView && child.getId() == R.id.display_cutout) { - ((DisplayCutoutView) child).setColor(mTintColor); - } - } - } - updateOverlayProviderViews(new Integer[] { mFaceScanningViewId, + R.id.display_cutout, + R.id.display_cutout_left, + R.id.display_cutout_right, + R.id.display_cutout_bottom, R.id.rounded_corner_top_left, R.id.rounded_corner_top_right, R.id.rounded_corner_bottom_left, @@ -1093,15 +1076,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab updateHwLayerRoundedCornerDrawable(); } updateLayoutParams(); - // update cutout view rotation - if (mCutoutViews != null) { - for (final DisplayCutoutView cutoutView: mCutoutViews) { - if (cutoutView == null) { - continue; - } - cutoutView.updateRotation(mRotation); - } - } // update all provider views inside overlay updateOverlayProviderViews(null); @@ -1120,46 +1094,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return mRoundedCornerFactory.getHasProviders(); } - private boolean isDefaultShownOverlayPos(@BoundsPosition int pos, - @Nullable DisplayCutout cutout) { - // for cutout is null or cutout with only waterfall. - final boolean emptyBoundsOrWaterfall = cutout == null || cutout.isBoundsEmpty(); - // Shows rounded corner on left and right overlays only when there is no top or bottom - // cutout. - final int rotatedTop = getBoundPositionFromRotation(BOUNDS_POSITION_TOP, mRotation); - final int rotatedBottom = getBoundPositionFromRotation(BOUNDS_POSITION_BOTTOM, mRotation); - if (emptyBoundsOrWaterfall || !cutout.getBoundingRectsAll()[rotatedTop].isEmpty() - || !cutout.getBoundingRectsAll()[rotatedBottom].isEmpty()) { - return pos == BOUNDS_POSITION_TOP || pos == BOUNDS_POSITION_BOTTOM; - } else { - return pos == BOUNDS_POSITION_LEFT || pos == BOUNDS_POSITION_RIGHT; - } - } - - private boolean shouldShowSwLayerRoundedCorner(@BoundsPosition int pos, - @Nullable DisplayCutout cutout) { - return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout) - && mHwcScreenDecorationSupport == null; - } - - private boolean shouldShowSwLayerPrivacyDot(@BoundsPosition int pos, - @Nullable DisplayCutout cutout) { - return isPrivacyDotEnabled() && isDefaultShownOverlayPos(pos, cutout); - } - - private boolean shouldShowSwLayerFaceScan(@BoundsPosition int pos, - @Nullable DisplayCutout cutout) { - return mFaceScanningFactory.getHasProviders() && isDefaultShownOverlayPos(pos, cutout); - } - - private boolean shouldShowSwLayerCutout(@BoundsPosition int pos, - @Nullable DisplayCutout cutout) { - final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll(); - final int rotatedPos = getBoundPositionFromRotation(pos, mRotation); - return (bounds != null && !bounds[rotatedPos].isEmpty() - && mHwcScreenDecorationSupport == null); - } - private boolean shouldOptimizeVisibility() { return (isPrivacyDotEnabled() || mFaceScanningFactory.getHasProviders()) && (mHwcScreenDecorationSupport != null @@ -1168,7 +1102,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } private boolean shouldDrawCutout() { - return shouldDrawCutout(mContext); + return mCutoutFactory.getHasProviders(); } static boolean shouldDrawCutout(Context context) { @@ -1284,7 +1218,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab paint.setColor(mColor); paint.setStyle(Paint.Style.FILL); - setId(R.id.display_cutout); if (DEBUG) { getViewTreeObserver().addOnDrawListener(() -> Log.i(TAG, getWindowTitleByPos(pos) + " drawn in rot " + mRotation)); @@ -1292,6 +1225,9 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } public void setColor(int color) { + if (color == mColor) { + return; + } mColor = color; paint.setColor(mColor); invalidate(); @@ -1299,6 +1235,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab @Override public void updateRotation(int rotation) { + // updateRotation() is called inside CutoutDecorProviderImpl::onReloadResAndMeasure() + // during onDisplayChanged. In order to prevent reloading cutout info in super class, + // check mRotation at first + if (rotation == mRotation) { + return; + } mRotation = rotation; super.updateRotation(rotation); } diff --git a/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderFactory.kt new file mode 100644 index 0000000000000..cbed21cf65d63 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderFactory.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 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.decor + +import android.content.res.Resources +import android.util.Log +import android.view.Display +import android.view.DisplayCutout +import android.view.DisplayInfo + +class CutoutDecorProviderFactory constructor( + private val res: Resources, + private val display: Display?, +) : DecorProviderFactory() { + + val displayInfo = DisplayInfo() + + override val hasProviders: Boolean + get() { + display?.getDisplayInfo(displayInfo) ?: run { + Log.w(TAG, "display is null, can't update displayInfo") + } + return DisplayCutout.getFillBuiltInDisplayCutout(res, displayInfo.uniqueId) + } + + override val providers: List + get() { + if (!hasProviders) { + return emptyList() + } + + return ArrayList().also { list -> + // We need to update displayInfo before using it, but it has already updated during + // accessing hasProviders field + displayInfo.displayCutout?.getBoundBaseOnCurrentRotation()?.let { bounds -> + for (bound in bounds) { + list.add( + CutoutDecorProviderImpl(bound.baseOnRotation0(displayInfo.rotation)) + ) + } + } + } + } +} + +private const val TAG = "CutoutDecorProviderFactory" diff --git a/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt new file mode 100644 index 0000000000000..991b54e8035ea --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2022 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.decor + +import android.content.Context +import android.view.DisplayCutout +import android.view.Surface +import android.view.View +import android.view.ViewGroup +import com.android.systemui.R +import com.android.systemui.ScreenDecorations.DisplayCutoutView + +class CutoutDecorProviderImpl( + @DisplayCutout.BoundsPosition override val alignedBound: Int +) : BoundDecorProvider() { + + override val viewId: Int = when (alignedBound) { + DisplayCutout.BOUNDS_POSITION_TOP -> R.id.display_cutout + DisplayCutout.BOUNDS_POSITION_LEFT -> R.id.display_cutout_left + DisplayCutout.BOUNDS_POSITION_RIGHT -> R.id.display_cutout_right + else -> R.id.display_cutout_bottom + } + + override fun inflateView( + context: Context, + parent: ViewGroup, + @Surface.Rotation rotation: Int, + tintColor: Int + ): View { + return DisplayCutoutView(context, alignedBound).also { view -> + view.id = viewId + view.setColor(tintColor) + parent.addView(view) + view.updateRotation(rotation) + } + } + + override fun onReloadResAndMeasure( + view: View, + reloadToken: Int, + @Surface.Rotation rotation: Int, + tintColor: Int, + displayUniqueId: String? + ) { + (view as? DisplayCutoutView)?.let { cutoutView -> + cutoutView.setColor(tintColor) + cutoutView.updateRotation(rotation) + cutoutView.onDisplayChanged(displayUniqueId) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt index de6d7278971c5..0681f50726a89 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt @@ -32,7 +32,7 @@ abstract class DecorProvider { abstract val viewId: Int /** The number of total aligned bounds */ - val numOfAlignedEdge: Int + val numOfAlignedBound: Int get() = alignedBounds.size /** The aligned bounds for the view which is created through inflateView() */ @@ -57,16 +57,8 @@ abstract class DecorProvider { @Surface.Rotation rotation: Int, tintColor: Int ): View -} -/** - * Split list to 2 list, and return it back as Pair<>. The providers on the first list contains this - * alignedBound element. The providers on the second list do not contain this alignedBound element - */ -fun List.partitionAlignedBound( - @DisplayCutout.BoundsPosition alignedBound: Int -): Pair, List> { - return partition { it.alignedBounds.contains(alignedBound) } + override fun toString() = "${javaClass.simpleName}{alignedBounds=$alignedBounds}" } /** @@ -94,3 +86,61 @@ abstract class BoundDecorProvider : DecorProvider() { listOf(alignedBound) } } + +/** + * Split list to 2 sub-lists, and return it back as Pair<>. The providers on the first list contains + * this alignedBound element. The providers on the second list do not contain this alignedBound + * element. + */ +fun List.partitionAlignedBound( + @DisplayCutout.BoundsPosition alignedBound: Int +): Pair, List> { + return partition { it.alignedBounds.contains(alignedBound) } +} + +/** + * Get the proper bound from DecorProvider list + * Time complexity: O(N), N is the number of providers + * + * Choose order + * 1. Return null if list is empty + * 2. If list contains BoundDecorProvider, return its alignedBound[0] because it is a must-have + * bound + * 3. Return the bound with most DecorProviders + */ +fun List.getProperBound(): Int? { + // Return null if list is empty + if (isEmpty()) { + return null + } + + // Choose alignedBounds[0] of BoundDecorProvider if any + val singleBoundProvider = firstOrNull { it.numOfAlignedBound == 1 } + if (singleBoundProvider != null) { + return singleBoundProvider.alignedBounds[0] + } + + // Return the bound with most DecorProviders + val boundCount = intArrayOf(0, 0, 0, 0) + for (provider in this) { + for (bound in provider.alignedBounds) { + boundCount[bound]++ + } + } + var maxCount = 0 + var maxCountBound: Int? = null + val bounds = arrayOf( + // Put top and bottom at first to get the highest priority to be chosen + DisplayCutout.BOUNDS_POSITION_TOP, + DisplayCutout.BOUNDS_POSITION_BOTTOM, + DisplayCutout.BOUNDS_POSITION_LEFT, + DisplayCutout.BOUNDS_POSITION_RIGHT + ) + for (bound in bounds) { + if (boundCount[bound] > maxCount) { + maxCountBound = bound + maxCount = boundCount[bound] + } + } + return maxCountBound +} diff --git a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt index dfb0b5aad9124..45b8a08a9b45c 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt @@ -114,7 +114,8 @@ class OverlayWindow(private val context: Context) { pw.println(" rootView=$rootView") for (i in 0 until rootView.childCount) { val child = rootView.getChildAt(i) - pw.println(" child[$i]=$child") + val provider = viewProviderMap[child.id]?.second + pw.println(" child[$i]=$child $provider") } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index 64a7986d05b1f..df10dfe9f1603 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -16,7 +16,6 @@ package com.android.systemui; import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM; import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; -import static android.view.DisplayCutout.BOUNDS_POSITION_LENGTH; import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT; import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; @@ -36,6 +35,7 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -49,7 +49,6 @@ import android.annotation.IdRes; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.TypedArray; -import android.graphics.Insets; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; @@ -60,7 +59,6 @@ import android.os.Handler; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; -import android.util.RotationUtils; import android.util.Size; import android.view.Display; import android.view.DisplayCutout; @@ -80,6 +78,8 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.decor.CornerDecorProvider; +import com.android.systemui.decor.CutoutDecorProviderFactory; +import com.android.systemui.decor.CutoutDecorProviderImpl; import com.android.systemui.decor.DecorProvider; import com.android.systemui.decor.DecorProviderFactory; import com.android.systemui.decor.FaceScanningOverlayProviderImpl; @@ -157,6 +157,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { @Mock private DisplayInfo mDisplayInfo; private PrivacyDotViewController.ShowingListener mPrivacyDotShowingListener; + @Mock + private CutoutDecorProviderFactory mCutoutFactory; + private List mMockCutoutList; @Before public void setup() { @@ -206,6 +209,11 @@ public class ScreenDecorationsTest extends SysuiTestCase { DisplayCutout.BOUNDS_POSITION_RIGHT, R.layout.privacy_dot_bottom_right)); + // Default no cutout + mMockCutoutList = new ArrayList<>(); + doAnswer(it -> !(mMockCutoutList.isEmpty())).when(mCutoutFactory).getHasProviders(); + doReturn(mMockCutoutList).when(mCutoutFactory).getProviders(); + mFaceScanningDecorProvider = spy(new FaceScanningOverlayProviderImpl( BOUNDS_POSITION_TOP, mAuthController, @@ -239,6 +247,11 @@ public class ScreenDecorationsTest extends SysuiTestCase { super.updateOverlayWindowVisibilityIfViewExists(view); mExecutor.runAllReady(); } + + @Override + protected CutoutDecorProviderFactory getCutoutFactory() { + return ScreenDecorationsTest.this.mCutoutFactory; + } }); mScreenDecorations.mDisplayInfo = mDisplayInfo; doReturn(1f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio(); @@ -429,11 +442,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testNoRounding_NoCutout_NoPrivacyDot_NoFaceScanning() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, false /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); - // no cutout - doReturn(null).when(mScreenDecorations).getCutout(); + // no cutout (default) mScreenDecorations.start(); // No views added. @@ -448,11 +459,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testNoRounding_NoCutout_PrivacyDot_NoFaceScanning() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); - // no cutout - doReturn(null).when(mScreenDecorations).getCutout(); + // no cutout (default) mScreenDecorations.start(); @@ -484,11 +493,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testRounding_NoCutout_NoPrivacyDot_NoFaceScanning() { setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 20 /* roundedPadding */, false /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); - // no cutout - doReturn(null).when(mScreenDecorations).getCutout(); + // no cutout (default) mScreenDecorations.start(); @@ -516,11 +523,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testRounding_NoCutout_PrivacyDot_NoFaceScanning() { setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 20 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); - // no cutout - doReturn(null).when(mScreenDecorations).getCutout(); + // no cutout (default) mScreenDecorations.start(); @@ -555,10 +560,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { /* roundedTopDrawable */, getTestsDrawable(com.android.systemui.tests.R.drawable.rounded3px) /* roundedBottomDrawable */, - 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); - // no cutout - doReturn(null).when(mScreenDecorations).getCutout(); + 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); + + // no cutout (default) mScreenDecorations.start(); // Size of corner view should same as rounded_corner_radius{_top|_bottom} @@ -574,11 +578,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { /* roundedTopDrawable */, getTestsDrawable(com.android.systemui.tests.R.drawable.rounded3px) /* roundedBottomDrawable */, - 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); - // no cutout - doReturn(null).when(mScreenDecorations).getCutout(); + // no cutout (default) mScreenDecorations.start(); View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView() @@ -611,13 +613,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { /* roundedTopDrawable */, getTestsDrawable(com.android.systemui.tests.R.drawable.rounded5px) /* roundedBottomDrawable */, - 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); // left cutout - final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT)); mScreenDecorations.start(); View topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() @@ -647,13 +646,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testNoRounding_CutoutShortEdge_NoPrivacyDot() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); // top cutout - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); // Top window is created for top cutout. @@ -671,13 +667,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testNoRounding_CutoutShortEdge_PrivacyDot() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); // top cutout - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); // Top window is created for top cutout. @@ -706,13 +699,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testNoRounding_CutoutLongEdge_NoPrivacyDot() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); // left cutout - final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT)); mScreenDecorations.start(); // Left window is created for left cutout. @@ -734,13 +724,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testNoRounding_CutoutLongEdge_PrivacyDot() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); // left cutout - final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT)); mScreenDecorations.start(); // Left window is created for left cutout. @@ -762,13 +749,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testRounding_CutoutShortEdge_NoPrivacyDot() { setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 20 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); // top cutout - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); // Top window is created for rounded corner and top cutout. @@ -791,13 +775,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testRounding_CutoutShortEdge_PrivacyDot() { setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 20 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); // top cutout - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); // Top window is created for rounded corner and top cutout. @@ -823,13 +804,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testRounding_CutoutLongEdge_NoPrivacyDot() { setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 20 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); // left cutout - final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT)); mScreenDecorations.start(); // Left window is created for rounded corner and left cutout. @@ -842,13 +820,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testRounding_CutoutLongEdge_PrivacyDot() { setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 20 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); // left cutout - final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT)); mScreenDecorations.start(); // Left window is created for rounded corner, left cutout, and privacy. @@ -863,13 +838,11 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testRounding_CutoutShortAndLongEdge_NoPrivacyDot() { setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 20 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); // top and left cutout - final Rect[] bounds = {new Rect(0, 50, 1, 60), new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(1, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT)); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); // Top window is created for rounded corner and top cutout. @@ -883,13 +856,11 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testRounding_CutoutShortAndLongEdge_PrivacyDot() { setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 20 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); // top and left cutout - final Rect[] bounds = {new Rect(0, 50, 1, 60), new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(1, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT)); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); // Top window is created for rounded corner and top cutout. @@ -905,21 +876,16 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout_NoPrivacyDot() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); // Set to short edge cutout(top). - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); verifyOverlaysExistAndAdded(false, true, false, false, View.VISIBLE); // Switch to long edge cutout(left). - final Rect[] newBounds = {new Rect(0, 50, 1, 60), null, null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), newBounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.set(0, new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT)); mScreenDecorations.onConfigurationChanged(new Configuration()); verifyOverlaysExistAndAdded(true, false, false, false, View.VISIBLE); @@ -929,13 +895,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout_PrivacyDot() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); // Set to short edge cutout(top). - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); @@ -943,9 +906,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(1)).setShowingListener(null); // Switch to long edge cutout(left). - final Rect[] newBounds = {new Rect(0, 50, 1, 60), null, null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), newBounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.set(0, new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT)); mScreenDecorations.onConfigurationChanged(new Configuration()); verifyOverlaysExistAndAdded(true, false, true, false, View.VISIBLE); @@ -973,20 +934,16 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testDelayedCutout_NoPrivacyDot() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, false /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); - // top cutout - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + // No cutout (default) mScreenDecorations.start(); verifyOverlaysExistAndAdded(false, false, false, false, null); - when(mContext.getResources().getBoolean( - com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout)) - .thenReturn(true); + // top cutout + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); + mScreenDecorations.onConfigurationChanged(new Configuration()); // Only top windows should be added. @@ -997,13 +954,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testDelayedCutout_PrivacyDot() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); - // top cutout - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + // no cutout (default) mScreenDecorations.start(); // Both top and bottom windows should be added with INVISIBLE because of only privacy dot, @@ -1015,9 +968,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(1)).setShowingListener( mScreenDecorations.mPrivacyDotShowingListener); - when(mContext.getResources().getBoolean( - com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout)) - .thenReturn(true); + // top cutout + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); + mScreenDecorations.onConfigurationChanged(new Configuration()); // Both top and bottom windows should be added with VISIBLE because of privacy dot and @@ -1043,8 +996,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { /* roundedTopDrawable */, getTestsDrawable(com.android.systemui.tests.R.drawable.rounded4px) /* roundedBottomDrawable */, - 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */, - false /* faceScanning*/); + 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning*/); mDisplayInfo.rotation = Surface.ROTATION_0; mScreenDecorations.start(); @@ -1058,8 +1010,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { /* roundedTopDrawable */, getTestsDrawable(com.android.systemui.tests.R.drawable.rounded5px) /* roundedBottomDrawable */, - 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */, - false /* faceScanning*/); + 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning*/); mDisplayInfo.rotation = Surface.ROTATION_270; mScreenDecorations.onConfigurationChanged(null); @@ -1072,8 +1023,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testOnlyRoundedCornerRadiusTop() { setupResources(0 /* radius */, 10 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); mScreenDecorations.start(); @@ -1094,8 +1044,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testOnlyRoundedCornerRadiusBottom() { setupResources(0 /* radius */, 0 /* radiusTop */, 20 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); mScreenDecorations.start(); @@ -1166,13 +1115,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testSupportHwcLayer_SwitchFrom_NotSupport() { setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); // top cutout - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); // should only inflate mOverlays when the hwc doesn't support screen decoration @@ -1195,16 +1141,13 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testNotSupportHwcLayer_SwitchFrom_Support() { setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport(); decorationSupport.format = PixelFormat.R_8; doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport(); // top cutout - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); // should only inflate hwc layer when the hwc supports screen decoration @@ -1234,16 +1177,13 @@ public class ScreenDecorationsTest extends SysuiTestCase { /* roundedTopDrawable */, getTestsDrawable(com.android.systemui.tests.R.drawable.rounded4px) /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */, - true /* faceScanning */); + 0 /* roundedPadding */, true /* privacyDot */, true /* faceScanning */); final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport(); decorationSupport.format = PixelFormat.R_8; doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport(); // top cutout - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); // Inflate top and bottom overlay with INVISIBLE because of only privacy dots on sw layer @@ -1277,11 +1217,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testAutoShowHideOverlayWindowWhenNoRoundedAndNoCutout() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */, - true /* faceScanning */); + 0 /* roundedPadding */, true /* privacyDot */, true /* faceScanning */); - // no cutout - doReturn(null).when(mScreenDecorations).getCutout(); + // no cutout (default) mScreenDecorations.start(); // Inflate top and bottom overlay with INVISIBLE because of only privacy dots on sw layer @@ -1315,16 +1253,13 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testHwcLayer_noPrivacyDot_noFaceScanning() { setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport(); decorationSupport.format = PixelFormat.R_8; doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport(); // top cutout - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); @@ -1337,16 +1272,13 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testHwcLayer_PrivacyDot_FaceScanning() { setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */, - true /* faceScanning */); + 0 /* roundedPadding */, true /* privacyDot */, true /* faceScanning */); final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport(); decorationSupport.format = PixelFormat.R_8; doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport(); // top cutout - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); @@ -1364,16 +1296,13 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testOnDisplayChanged_hwcLayer() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport(); decorationSupport.format = PixelFormat.R_8; doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport(); // top cutout - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); @@ -1390,18 +1319,16 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testOnDisplayChanged_nonHwcLayer() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); // top cutout - final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null}; - doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds)) - .when(mScreenDecorations).getCutout(); + mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP)); mScreenDecorations.start(); - final ScreenDecorations.DisplayCutoutView cutoutView = - mScreenDecorations.mCutoutViews[BOUNDS_POSITION_TOP]; + final ScreenDecorations.DisplayCutoutView cutoutView = (ScreenDecorations.DisplayCutoutView) + mScreenDecorations.getOverlayView(R.id.display_cutout); + assertNotNull(cutoutView); spyOn(cutoutView); doReturn(mDisplay).when(cutoutView).getDisplay(); @@ -1414,8 +1341,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testHasSameProvidersWithNullOverlays() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, false /* fillCutout */, false /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); mScreenDecorations.start(); @@ -1433,8 +1359,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { public void testHasSameProvidersWithPrivacyDots() { setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, null /* roundedTopDrawable */, null /* roundedBottomDrawable */, - 0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */, - false /* faceScanning */); + 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */); mScreenDecorations.start(); @@ -1471,7 +1396,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { private void setupResources(int radius, int radiusTop, int radiusBottom, @Nullable Drawable roundedTopDrawable, @Nullable Drawable roundedBottomDrawable, - int roundedPadding, boolean fillCutout, boolean privacyDot, boolean faceScanning) { + int roundedPadding, boolean privacyDot, boolean faceScanning) { mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.array.config_displayUniqueIdArray, new String[]{}); @@ -1511,8 +1436,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { } mContext.getOrCreateTestableResources().addOverride( R.dimen.rounded_corner_content_padding, roundedPadding); - mContext.getOrCreateTestableResources().addOverride( - com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout); mPrivacyDecorProviders = new ArrayList<>(); if (privacyDot) { @@ -1531,19 +1454,4 @@ public class ScreenDecorationsTest extends SysuiTestCase { when(mFaceScanningProviderFactory.getProviders()).thenReturn(mFaceScanningProviders); when(mFaceScanningProviderFactory.getHasProviders()).thenReturn(faceScanning); } - - private DisplayCutout getDisplayCutoutForRotation(Insets safeInsets, Rect[] cutoutBounds) { - final int rotation = mContext.getDisplay().getRotation(); - final Insets insets = RotationUtils.rotateInsets(safeInsets, rotation); - final Rect[] sorted = new Rect[BOUNDS_POSITION_LENGTH]; - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - final int rotatedPos = ScreenDecorations.getBoundPositionFromRotation(i, rotation); - if (cutoutBounds[i] != null) { - RotationUtils.rotateBounds(cutoutBounds[i], new Rect(0, 0, 100, 200), rotation); - } - sorted[rotatedPos] = cutoutBounds[i]; - } - return new DisplayCutout(insets, sorted[BOUNDS_POSITION_LEFT], sorted[BOUNDS_POSITION_TOP], - sorted[BOUNDS_POSITION_RIGHT], sorted[BOUNDS_POSITION_BOTTOM]); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt new file mode 100644 index 0000000000000..1040ec453a7c1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2022 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.decor + +import android.graphics.Insets +import android.graphics.Rect +import android.testing.AndroidTestingRunner +import android.testing.TestableResources +import android.util.RotationUtils +import android.util.Size +import android.view.Display +import android.view.DisplayCutout +import android.view.DisplayCutout.BOUNDS_POSITION_LENGTH +import android.view.DisplayInfo +import android.view.Surface +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.any +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.doAnswer +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class CutoutDecorProviderFactoryTest : SysuiTestCase() { + + @Mock private lateinit var display: Display + private var testableRes: TestableResources? = null + private lateinit var factory: CutoutDecorProviderFactory + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + testableRes = mContext.orCreateTestableResources + factory = CutoutDecorProviderFactory(testableRes!!.resources, display) + } + + private fun setupFillCutout(fillCutout: Boolean) { + testableRes!!.addOverride( + com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout + ) + } + + private fun setupDisplayInfo( + displayCutout: DisplayCutout? = null, + @Surface.Rotation rotation: Int = Surface.ROTATION_0, + displayId: Int = -1 + ) { + doAnswer { + it.getArgument(0).let { info -> + info.displayCutout = displayCutout + info.rotation = rotation + info.displayId = displayId + } + true + }.`when`(display).getDisplayInfo(any()) + } + + private fun getCutout( + safeInsets: Insets, + cutoutBounds: Array, + @Surface.Rotation rotation: Int = Surface.ROTATION_0, + cutoutParentSizeForRotate: Size = Size(100, 200) + ): DisplayCutout { + val insets = RotationUtils.rotateInsets(safeInsets, rotation) + val sorted = arrayOfNulls(BOUNDS_POSITION_LENGTH) + for (pos in 0 until BOUNDS_POSITION_LENGTH) { + val rotatedPos = (pos - rotation + BOUNDS_POSITION_LENGTH) % BOUNDS_POSITION_LENGTH + if (cutoutBounds[pos] != null) { + RotationUtils.rotateBounds( + cutoutBounds[pos], + cutoutParentSizeForRotate.width, + cutoutParentSizeForRotate.height, + rotation + ) + } + sorted[rotatedPos] = cutoutBounds[pos] + } + return DisplayCutout( + insets, + sorted[DisplayCutout.BOUNDS_POSITION_LEFT], + sorted[DisplayCutout.BOUNDS_POSITION_TOP], + sorted[DisplayCutout.BOUNDS_POSITION_RIGHT], + sorted[DisplayCutout.BOUNDS_POSITION_BOTTOM] + ) + } + + @Test + fun testGetNothingIfNoCutout() { + setupFillCutout(false) + + Assert.assertFalse(factory.hasProviders) + Assert.assertEquals(0, factory.providers.size) + } + + @Test + fun testGetTopCutoutProvider() { + setupFillCutout(true) + setupDisplayInfo( + getCutout( + safeInsets = Insets.of(0, 1, 0, 0), + cutoutBounds = arrayOf(null, Rect(9, 0, 10, 1), null, null) + ) + ) + + Assert.assertTrue(factory.hasProviders) + + val providers = factory.providers + Assert.assertEquals(1, providers.size) + Assert.assertEquals(1, providers[0].numOfAlignedBound) + Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_TOP, providers[0].alignedBounds[0]) + } + + @Test + fun testGetBottomCutoutProviderOnLandscape() { + setupFillCutout(true) + setupDisplayInfo( + getCutout( + safeInsets = Insets.of(0, 0, 0, 1), + cutoutBounds = arrayOf(null, null, null, Rect(45, 199, 55, 200)), + rotation = Surface.ROTATION_90 + ), + Surface.ROTATION_90 + ) + + Assert.assertTrue(factory.hasProviders) + + val providers = factory.providers + Assert.assertEquals(1, providers.size) + Assert.assertEquals(1, providers[0].numOfAlignedBound) + Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_BOTTOM, providers[0].alignedBounds[0]) + } + + @Test + fun testGetLeftCutoutProviderOnSeascape() { + setupFillCutout(true) + setupDisplayInfo( + getCutout( + safeInsets = Insets.of(1, 0, 0, 0), + cutoutBounds = arrayOf(Rect(0, 20, 1, 40), null, null, null), + rotation = Surface.ROTATION_270 + ), + Surface.ROTATION_270 + ) + + Assert.assertTrue(factory.hasProviders) + + val providers = factory.providers + Assert.assertEquals(1, providers.size) + Assert.assertEquals(1, providers[0].numOfAlignedBound) + Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_LEFT, providers[0].alignedBounds[0]) + } + + @Test + fun testGetTopRightCutoutProviderOnReverse() { + setupFillCutout(true) + setupDisplayInfo( + getCutout( + safeInsets = Insets.of(0, 1, 1, 0), + cutoutBounds = arrayOf( + null, + Rect(9, 0, 10, 1), + Rect(99, 40, 100, 60), + null + ), + rotation = Surface.ROTATION_180 + ), + Surface.ROTATION_180 + ) + + Assert.assertTrue(factory.hasProviders) + + val providers = factory.providers + Assert.assertEquals(2, providers.size) + Assert.assertEquals(1, providers[0].numOfAlignedBound) + Assert.assertEquals(1, providers[1].numOfAlignedBound) + providers.sortedBy { it.alignedBounds[0] }.let { + Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_TOP, it[0].alignedBounds[0]) + Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_RIGHT, it[1].alignedBounds[0]) + } + } +}