From cb4664745cfb84d68ec80826a21c5210be251d80 Mon Sep 17 00:00:00 2001 From: Milton Wu Date: Thu, 10 Mar 2022 16:01:24 +0800 Subject: [PATCH] Move rounded corner to provider Move rounded corners code from ScreenDecorations. 1. Move business logic to RoundedCornerDecorProviderFactory. 2. Move view and inflating into RounedCornerDecorProviderImpl. Bug: 218951516 Test: atest ScreenDecorationsTest \ OverlayWindowTest \ PrivacyDotDecorProviderFactoryTest \ RoundedCornerDecorProviderFactoryTest \ RoundedCornerResDelegateTest Test: Manually testing rounded corner when display size, rotation changed on normal devices, foldable devices, and hardware-overlay devices Change-Id: I60e8a0ceaca19dc45117fb98db45f4063cbfe1b3 --- .../res/layout/rounded_corners_bottom.xml | 40 --- .../res/layout/rounded_corners_top.xml | 40 --- packages/SystemUI/res/values/ids.xml | 6 + .../android/systemui/ScreenDecorHwcLayer.kt | 7 +- .../android/systemui/ScreenDecorations.java | 271 +++++------------- .../android/systemui/decor/DecorProvider.kt | 15 +- .../android/systemui/decor/OverlayWindow.kt | 60 ++-- .../decor/PrivacyDotDecorProviderFactory.kt | 14 +- .../RoundedCornerDecorProviderFactory.kt | 86 ++++++ .../decor/RoundedCornerDecorProviderImpl.kt | 192 +++++++++++++ .../decor/RoundedCornerResDelegate.kt | 58 ++-- .../systemui/ScreenDecorationsTest.java | 176 +++++++----- .../systemui/decor/OverlayWindowTest.kt | 116 ++++---- .../PrivacyDotDecorProviderFactoryTest.kt | 2 - .../RoundedCornerDecorProviderFactoryTest.kt | 142 +++++++++ .../decor/RoundedCornerResDelegateTest.kt | 43 ++- 16 files changed, 798 insertions(+), 470 deletions(-) delete mode 100644 packages/SystemUI/res/layout/rounded_corners_bottom.xml delete mode 100644 packages/SystemUI/res/layout/rounded_corners_top.xml create mode 100644 packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt create mode 100644 packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt diff --git a/packages/SystemUI/res/layout/rounded_corners_bottom.xml b/packages/SystemUI/res/layout/rounded_corners_bottom.xml deleted file mode 100644 index bb6d4bddf25ad..0000000000000 --- a/packages/SystemUI/res/layout/rounded_corners_bottom.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/packages/SystemUI/res/layout/rounded_corners_top.xml b/packages/SystemUI/res/layout/rounded_corners_top.xml deleted file mode 100644 index 46648c88d921e..0000000000000 --- a/packages/SystemUI/res/layout/rounded_corners_top.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 926734c2749f0..096b9a0c47905 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -167,5 +167,11 @@ + + + + + + diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt index 498e71546bed7..d163b26710d3d 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt @@ -357,10 +357,15 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec * Update the rounded corner size. */ fun updateRoundedCornerSize(top: Int, bottom: Int) { + if (roundedCornerTopSize == top && roundedCornerBottomSize == bottom) { + return + } roundedCornerTopSize = top roundedCornerBottomSize = bottom updateRoundedCornerDrawableBounds() - invalidate() + + // Use requestLayout() to trigger transparent region recalculated + requestLayout() } private fun updateRoundedCornerDrawableBounds() { diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 2f5292cec9098..5de09b13bf6a6 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -77,6 +77,7 @@ import com.android.systemui.decor.DecorProviderFactory; import com.android.systemui.decor.DecorProviderKt; import com.android.systemui.decor.OverlayWindow; import com.android.systemui.decor.PrivacyDotDecorProviderFactory; +import com.android.systemui.decor.RoundedCornerDecorProviderFactory; import com.android.systemui.decor.RoundedCornerResDelegate; import com.android.systemui.qs.SettingObserver; import com.android.systemui.settings.UserTracker; @@ -138,6 +139,9 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab @VisibleForTesting protected RoundedCornerResDelegate mRoundedCornerResDelegate; @VisibleForTesting + protected DecorProviderFactory mRoundedCornerFactory; + private int mProviderRefreshToken = 0; + @VisibleForTesting protected OverlayWindow[] mOverlays = null; @Nullable private DisplayCutoutView[] mCutoutViews; @@ -292,11 +296,11 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mDisplayUniqueId = mContext.getDisplay().getUniqueId(); mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(), mDisplayUniqueId); + mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate); mWindowManager = mContext.getSystemService(WindowManager.class); mDisplayManager = mContext.getSystemService(DisplayManager.class); mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); - updateRoundedCornerDrawable(); - updateRoundedCornerRadii(); + updateHwLayerRoundedCornerDrawable(); setupDecorations(); setupCameraListener(); @@ -348,7 +352,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab final String newUniqueId = mContext.getDisplay().getUniqueId(); if (!Objects.equals(newUniqueId, mDisplayUniqueId)) { mDisplayUniqueId = newUniqueId; - mRoundedCornerResDelegate.reloadAll(newUniqueId); + mRoundedCornerResDelegate.updateDisplayUniqueId(newUniqueId, null); final DisplayDecorationSupport newScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); // When the value of mSupportHwcScreenDecoration is changed, re-setup the whole @@ -359,12 +363,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab setupDecorations(); return; } - updateRoundedCornerDrawable(); + updateHwLayerRoundedCornerDrawable(); } if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.onDisplayChanged(displayId); } - updateOrientation(); + updateView(); } }; @@ -406,22 +410,22 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } private void setupDecorations() { - List decorProviders = mDotFactory.getProviders(); - - if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) { + if (hasRoundedCorners() || shouldDrawCutout() || isPrivacyDotEnabled()) { + List decorProviders = new ArrayList<>(mDotFactory.getProviders()); if (mHwcScreenDecorationSupport != null) { createHwcOverlay(); } else { removeHwcOverlay(); + decorProviders.addAll(mRoundedCornerFactory.getProviders()); } final DisplayCutout cutout = getCutout(); for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (shouldShowCutout(i, cutout) || shouldShowRoundedCorner(i, cutout) - || shouldShowPrivacyDot(i, cutout)) { + if (shouldShowSwLayerCutout(i, cutout) || shouldShowSwLayerRoundedCorner(i, cutout) + || shouldShowSwLayerPrivacyDot(i, cutout)) { Pair, List> pair = DecorProviderKt.partitionAlignedBound(decorProviders, i); decorProviders = pair.getSecond(); - createOverlay(i, cutout, pair.getFirst()); + createOverlay(i, pair.getFirst()); } else { removeOverlay(i); } @@ -522,7 +526,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab private void createOverlay( @BoundsPosition int pos, - @Nullable DisplayCutout cutout, @NonNull List decorProviders) { if (mOverlays == null) { mOverlays = new OverlayWindow[BOUNDS_POSITION_LENGTH]; @@ -547,7 +550,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mCutoutViews[pos] = new DisplayCutoutView(mContext, pos); mCutoutViews[pos].setColor(mTintColor); overlayView.addView(mCutoutViews[pos]); - updateView(pos, cutout); + mCutoutViews[pos].updateRotation(mRotation); } mWindowManager.addView(overlayView, getWindowLayoutParams(pos)); @@ -603,7 +606,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab private OverlayWindow overlayForPosition( @BoundsPosition int pos, @NonNull List decorProviders) { - final OverlayWindow currentOverlay = new OverlayWindow(LayoutInflater.from(mContext), pos); + final OverlayWindow currentOverlay = new OverlayWindow(mContext); decorProviders.forEach(provider -> { removeOverlayView(provider.getViewId()); currentOverlay.addDecorProvider(provider, mRotation); @@ -617,22 +620,16 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return currentOverlay; } - private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { - if (mOverlays == null || mOverlays[pos] == null || mHwcScreenDecorationSupport != null) { + private void updateView() { + if (mOverlays == null) { return; } - - // update rounded corner view rotation - updateRoundedCornerView(pos, R.id.left, cutout); - updateRoundedCornerView(pos, R.id.right, cutout); - updateRoundedCornerSize( - mRoundedCornerResDelegate.getTopRoundedSize(), - mRoundedCornerResDelegate.getBottomRoundedSize()); - updateRoundedCornerImageView(); - - // update cutout view rotation - if (mCutoutViews != null && mCutoutViews[pos] != null) { - mCutoutViews[pos].updateRotation(mRotation); + ++mProviderRefreshToken; + for (final OverlayWindow overlay: mOverlays) { + if (overlay == null) { + continue; + } + overlay.onReloadResAndMeasure(null, mProviderRefreshToken, mRotation, mDisplayUniqueId); } } @@ -806,7 +803,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab int oldRotation = mRotation; mPendingRotationChange = false; updateOrientation(); - updateRoundedCornerRadii(); if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation); setupDecorations(); if (mOverlays != null) { @@ -866,109 +862,32 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mDotViewController.setNewRotation(newRotation); } - if (mPendingRotationChange) { - return; - } - if (newRotation != mRotation) { + if (!mPendingRotationChange && newRotation != mRotation) { mRotation = newRotation; if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.pendingRotationChange = false; mScreenDecorHwcLayer.updateRotation(mRotation); + updateHwLayerRoundedCornerSize(); + updateHwLayerRoundedCornerDrawable(); } - if (mOverlays != null) { - updateLayoutParams(); - final DisplayCutout cutout = getCutout(); - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { + updateLayoutParams(); + // update cutout view rotation + if (mCutoutViews != null) { + for (final DisplayCutoutView cutoutView: mCutoutViews) { + if (cutoutView == null) { continue; } - updateView(i, cutout); + cutoutView.updateRotation(mRotation); } } } + + // update views + updateView(); } - private void updateRoundedCornerRadii() { - // We should eventually move to just using the intrinsic size of the drawables since - // they should be sized to the exact pixels they want to cover. Therefore I'm purposely not - // upgrading all of the configs to contain (width, height) pairs. Instead assume that a - // device configured using the single integer config value is okay with drawing the corners - // as a square - final Size oldRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize(); - final Size oldRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize(); - mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId); - final Size newRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize(); - final Size newRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize(); - - if (oldRoundedDefaultTop.getWidth() != newRoundedDefaultTop.getWidth() - || oldRoundedDefaultBottom.getWidth() != newRoundedDefaultBottom.getWidth()) { - onTuningChanged(SIZE, null); - } - } - - private void updateRoundedCornerView(@BoundsPosition int pos, int id, - @Nullable DisplayCutout cutout) { - final View rounded = mOverlays[pos].getRootView().findViewById(id); - if (rounded == null) { - return; - } - rounded.setVisibility(View.GONE); - if (shouldShowRoundedCorner(pos, cutout)) { - final int gravity = getRoundedCornerGravity(pos, id == R.id.left); - ((FrameLayout.LayoutParams) rounded.getLayoutParams()).gravity = gravity; - setRoundedCornerOrientation(rounded, gravity); - rounded.setVisibility(View.VISIBLE); - } - } - - private int getRoundedCornerGravity(@BoundsPosition int pos, boolean isStart) { - final int rotatedPos = getBoundPositionFromRotation(pos, mRotation); - switch (rotatedPos) { - case BOUNDS_POSITION_LEFT: - return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.BOTTOM | Gravity.LEFT; - case BOUNDS_POSITION_TOP: - return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.TOP | Gravity.RIGHT; - case BOUNDS_POSITION_RIGHT: - return isStart ? Gravity.TOP | Gravity.RIGHT : Gravity.BOTTOM | Gravity.RIGHT; - case BOUNDS_POSITION_BOTTOM: - return isStart ? Gravity.BOTTOM | Gravity.LEFT : Gravity.BOTTOM | Gravity.RIGHT; - default: - throw new IllegalArgumentException("Incorrect position: " + rotatedPos); - } - } - - /** - * Configures the rounded corner drawable's view matrix based on the gravity. - * - * The gravity describes which corner to configure for, and the drawable we are rotating is - * assumed to be oriented for the top-left corner of the device regardless of the target corner. - * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or - * y-axis for the top-right and bottom-left corners. - */ - private void setRoundedCornerOrientation(View corner, int gravity) { - corner.setRotation(0); - corner.setScaleX(1); - corner.setScaleY(1); - switch (gravity) { - case Gravity.TOP | Gravity.LEFT: - return; - case Gravity.TOP | Gravity.RIGHT: - corner.setScaleX(-1); // flip X axis - return; - case Gravity.BOTTOM | Gravity.LEFT: - corner.setScaleY(-1); // flip Y axis - return; - case Gravity.BOTTOM | Gravity.RIGHT: - corner.setRotation(180); - return; - default: - throw new IllegalArgumentException("Unsupported gravity: " + gravity); - } - } private boolean hasRoundedCorners() { - return mRoundedCornerResDelegate.getBottomRoundedSize().getWidth() > 0 - || mRoundedCornerResDelegate.getTopRoundedSize().getWidth() > 0 - || mRoundedCornerResDelegate.isMultipleRadius(); + return mRoundedCornerFactory.getHasProviders(); } private boolean isDefaultShownOverlayPos(@BoundsPosition int pos, @@ -987,17 +906,19 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } } - private boolean shouldShowRoundedCorner(@BoundsPosition int pos, + private boolean shouldShowSwLayerRoundedCorner(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout) && mHwcScreenDecorationSupport == null; } - private boolean shouldShowPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { + private boolean shouldShowSwLayerPrivacyDot(@BoundsPosition int pos, + @Nullable DisplayCutout cutout) { return isPrivacyDotEnabled() && isDefaultShownOverlayPos(pos, cutout); } - private boolean shouldShowCutout(@BoundsPosition int pos, @Nullable DisplayCutout 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() @@ -1032,54 +953,33 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return; } mExecutor.execute(() -> { - if (mOverlays == null) return; - if (SIZE.equals(key)) { - if (newValue != null) { - try { - mRoundedCornerResDelegate.updateTuningSizeFactor( - Integer.parseInt(newValue)); - } catch (Exception e) { - } + if (mOverlays == null || !SIZE.equals(key)) { + return; + } + ++mProviderRefreshToken; + try { + final int sizeFactor = Integer.parseInt(newValue); + mRoundedCornerResDelegate.updateTuningSizeFactor(sizeFactor, mProviderRefreshToken); + } catch (NumberFormatException e) { + mRoundedCornerResDelegate.updateTuningSizeFactor(null, mProviderRefreshToken); + } + Integer[] filterIds = { + 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; } - updateRoundedCornerSize( - mRoundedCornerResDelegate.getTopRoundedSize(), - mRoundedCornerResDelegate.getBottomRoundedSize()); + overlay.onReloadResAndMeasure(filterIds, mProviderRefreshToken, mRotation, + mDisplayUniqueId); } + updateHwLayerRoundedCornerSize(); }); } - private void updateRoundedCornerDrawable() { - mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId); - updateRoundedCornerImageView(); - } - - private void updateRoundedCornerImageView() { - final Drawable top = mRoundedCornerResDelegate.getTopRoundedDrawable(); - final Drawable bottom = mRoundedCornerResDelegate.getBottomRoundedDrawable(); - - if (mScreenDecorHwcLayer != null) { - mScreenDecorHwcLayer.updateRoundedCornerDrawable(top, bottom); - return; - } - - if (mOverlays == null) { - return; - } - final ColorStateList colorStateList = ColorStateList.valueOf(mTintColor); - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { - continue; - } - final ViewGroup overlayView = mOverlays[i].getRootView(); - ((ImageView) overlayView.findViewById(R.id.left)).setImageTintList(colorStateList); - ((ImageView) overlayView.findViewById(R.id.right)).setImageTintList(colorStateList); - ((ImageView) overlayView.findViewById(R.id.left)).setImageDrawable( - isTopRoundedCorner(i, R.id.left) ? top : bottom); - ((ImageView) overlayView.findViewById(R.id.right)).setImageDrawable( - isTopRoundedCorner(i, R.id.right) ? top : bottom); - } - } - private void updateHwLayerRoundedCornerDrawable() { if (mScreenDecorHwcLayer == null) { return; @@ -1094,25 +994,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mScreenDecorHwcLayer.updateRoundedCornerDrawable(topDrawable, bottomDrawable); } - @VisibleForTesting - boolean isTopRoundedCorner(@BoundsPosition int pos, int id) { - switch (pos) { - case BOUNDS_POSITION_LEFT: - case BOUNDS_POSITION_RIGHT: - if (mRotation == ROTATION_270) { - return id == R.id.left ? false : true; - } else { - return id == R.id.left ? true : false; - } - case BOUNDS_POSITION_TOP: - return true; - case BOUNDS_POSITION_BOTTOM: - return false; - default: - throw new IllegalArgumentException("Unknown bounds position"); - } - } - private void updateHwLayerRoundedCornerSize() { if (mScreenDecorHwcLayer == null) { return; @@ -1124,28 +1005,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mScreenDecorHwcLayer.updateRoundedCornerSize(topWidth, bottomWidth); } - private void updateRoundedCornerSize(Size sizeTop, Size sizeBottom) { - - if (mScreenDecorHwcLayer != null) { - mScreenDecorHwcLayer.updateRoundedCornerSize(sizeTop.getWidth(), sizeBottom.getWidth()); - return; - } - - if (mOverlays == null) { - return; - } - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { - continue; - } - final ViewGroup overlayView = mOverlays[i].getRootView(); - setSize(overlayView.findViewById(R.id.left), - isTopRoundedCorner(i, R.id.left) ? sizeTop : sizeBottom); - setSize(overlayView.findViewById(R.id.right), - isTopRoundedCorner(i, R.id.right) ? sizeTop : sizeBottom); - } - } - @VisibleForTesting protected void setSize(View view, Size pixelSize) { LayoutParams params = view.getLayoutParams(); diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt index 3543bb4ab9e96..03ee8b11ab415 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.decor +import android.content.Context import android.view.DisplayCutout -import android.view.LayoutInflater import android.view.Surface import android.view.View import android.view.ViewGroup @@ -38,9 +38,20 @@ abstract class DecorProvider { /** The aligned bounds for the view which is created through inflateView() */ abstract val alignedBounds: List + /** + * Called when res info changed. + * Child provider needs to implement it if its view needs to be updated. + */ + abstract fun onReloadResAndMeasure( + view: View, + reloadToken: Int, + @Surface.Rotation rotation: Int, + displayUniqueId: String? = null + ) + /** Inflate view into parent as current rotation */ abstract fun inflateView( - inflater: LayoutInflater, + context: Context, parent: ViewGroup, @Surface.Rotation rotation: Int ): View diff --git a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt index 9f8679cdea4a9..f38ff14726ca5 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt @@ -16,31 +16,22 @@ package com.android.systemui.decor import android.annotation.IdRes -import android.view.DisplayCutout -import android.view.LayoutInflater +import android.content.Context import android.view.Surface import android.view.View import android.view.ViewGroup -import com.android.systemui.R -import java.util.HashMap +import com.android.systemui.RegionInterceptingFrameLayout -class OverlayWindow(private val layoutInflater: LayoutInflater, private val pos: Int) { +class OverlayWindow(private val context: Context) { - private val layoutId: Int - get() { - return if (pos == DisplayCutout.BOUNDS_POSITION_LEFT || - pos == DisplayCutout.BOUNDS_POSITION_TOP) { - R.layout.rounded_corners_top - } else { - R.layout.rounded_corners_bottom - } - } + val rootView = RegionInterceptingFrameLayout(context) as ViewGroup + private val viewProviderMap = mutableMapOf>() - val rootView = layoutInflater.inflate(layoutId, null) as ViewGroup - private val viewProviderMap: MutableMap> = HashMap() - - fun addDecorProvider(decorProvider: DecorProvider, @Surface.Rotation rotation: Int) { - val view = decorProvider.inflateView(layoutInflater, rootView, rotation) + fun addDecorProvider( + decorProvider: DecorProvider, + @Surface.Rotation rotation: Int + ) { + val view = decorProvider.inflateView(context, rootView, rotation) viewProviderMap[decorProvider.viewId] = Pair(view, decorProvider) } @@ -56,4 +47,35 @@ class OverlayWindow(private val layoutInflater: LayoutInflater, private val pos: viewProviderMap.remove(id) } } + + /** + * Apply new configuration info into views. + * @param filterIds target view ids. Apply to all if null. + * @param rotation current or new rotation direction. + * @param displayUniqueId new displayUniqueId if any. + */ + fun onReloadResAndMeasure( + filterIds: Array? = null, + reloadToken: Int, + @Surface.Rotation rotation: Int, + displayUniqueId: String? = null + ) { + filterIds?.forEach { id -> + viewProviderMap[id]?.let { + it.second.onReloadResAndMeasure( + view = it.first, + reloadToken = reloadToken, + displayUniqueId = displayUniqueId, + rotation = rotation) + } + } ?: run { + viewProviderMap.values.forEach { + it.second.onReloadResAndMeasure( + view = it.first, + reloadToken = reloadToken, + displayUniqueId = displayUniqueId, + rotation = rotation) + } + } + } } \ 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 7afd7e0eedc51..136f135af7599 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt @@ -16,6 +16,7 @@ package com.android.systemui.decor +import android.content.Context import android.content.res.Resources import android.view.DisplayCutout import android.view.LayoutInflater @@ -76,12 +77,21 @@ class PrivacyDotCornerDecorProviderImpl( private val layoutId: Int ) : CornerDecorProvider() { + override fun onReloadResAndMeasure( + view: View, + reloadToken: Int, + rotation: Int, + displayUniqueId: String? + ) { + // Do nothing here because it is handled inside PrivacyDotViewController + } + override fun inflateView( - inflater: LayoutInflater, + context: Context, parent: ViewGroup, @Surface.Rotation rotation: Int ): View { - inflater.inflate(layoutId, parent, true) + 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/RoundedCornerDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt new file mode 100644 index 0000000000000..4388b8b3b92dd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt @@ -0,0 +1,86 @@ +/* + * 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.view.DisplayCutout +import com.android.systemui.R + +class RoundedCornerDecorProviderFactory( + private val roundedCornerResDelegate: RoundedCornerResDelegate +) : DecorProviderFactory() { + + override val hasProviders: Boolean + get() = roundedCornerResDelegate.run { + // We don't consider isMultipleRadius here because it makes no sense if size is zero. + topRoundedSize.width > 0 || bottomRoundedSize.width > 0 + } + + override val providers: List + get() { + val hasTop = roundedCornerResDelegate.topRoundedSize.width > 0 + val hasBottom = roundedCornerResDelegate.bottomRoundedSize.width > 0 + return when { + hasTop && hasBottom -> listOf( + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate) + ) + hasTop -> listOf( + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate) + ) + hasBottom -> listOf( + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate) + ) + else -> emptyList() + } + } +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt new file mode 100644 index 0000000000000..90ff950406b46 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt @@ -0,0 +1,192 @@ +/* + * 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.Gravity +import android.view.Surface +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import com.android.systemui.R + +class RoundedCornerDecorProviderImpl( + override val viewId: Int, + @DisplayCutout.BoundsPosition override val alignedBound1: Int, + @DisplayCutout.BoundsPosition override val alignedBound2: Int, + private val roundedCornerResDelegate: RoundedCornerResDelegate +) : CornerDecorProvider() { + + private val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + + override fun inflateView( + context: Context, + parent: ViewGroup, + @Surface.Rotation rotation: Int + ): View { + return ImageView(context).also { view -> + // View + view.id = viewId + initView(view, rotation) + + // LayoutParams + val layoutSize = if (isTop) { + roundedCornerResDelegate.topRoundedSize + } else { + roundedCornerResDelegate.bottomRoundedSize + } + val params = FrameLayout.LayoutParams( + 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) { + view.setRoundedCornerImage(roundedCornerResDelegate, isTop) + view.adjustRotation(alignedBounds, rotation) + view.setColorFilter(IMAGE_TINT_COLOR) + } + + override fun onReloadResAndMeasure( + view: View, + reloadToken: Int, + @Surface.Rotation rotation: Int, + displayUniqueId: String? + ) { + roundedCornerResDelegate.updateDisplayUniqueId(displayUniqueId, reloadToken) + + initView((view as ImageView), rotation) + + val layoutSize = if (isTop) { + roundedCornerResDelegate.topRoundedSize + } else { + roundedCornerResDelegate.bottomRoundedSize + } + (view.layoutParams as FrameLayout.LayoutParams).let { + it.width = layoutSize.width + it.height = layoutSize.height + it.gravity = alignedBound1.toLayoutGravity(rotation) or + alignedBound2.toLayoutGravity(rotation) + view.setLayoutParams(it) + } + } +} + +private const val IMAGE_TINT_COLOR: Int = 0xFF000000.toInt() + +@DisplayCutout.BoundsPosition +private fun Int.toLayoutGravity(@Surface.Rotation rotation: Int): Int = when (rotation) { + Surface.ROTATION_0 -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.LEFT + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.TOP + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.RIGHT + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.BOTTOM + } + Surface.ROTATION_90 -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.BOTTOM + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.LEFT + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.TOP + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT + } + Surface.ROTATION_270 -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.TOP + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.RIGHT + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.BOTTOM + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT + } + else /* Surface.ROTATION_180 */ -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.RIGHT + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.BOTTOM + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.LEFT + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.TOP + } +} + +private fun ImageView.setRoundedCornerImage( + resDelegate: RoundedCornerResDelegate, + isTop: Boolean +) { + val drawable = if (isTop) + resDelegate.topRoundedDrawable + else + resDelegate.bottomRoundedDrawable + + if (drawable != null) { + setImageDrawable(drawable) + } else { + setImageResource( + if (isTop) + R.drawable.rounded_corner_top + else + R.drawable.rounded_corner_bottom + ) + } +} + +/** + * Configures the rounded corner drawable's view matrix based on the gravity. + * + * The gravity describes which corner to configure for, and the drawable we are rotating is assumed + * to be oriented for the top-left corner of the device regardless of the target corner. + * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or + * y-axis for the top-right and bottom-left corners. + */ +private fun ImageView.adjustRotation(alignedBounds: List, @Surface.Rotation rotation: Int) { + var newRotation = 0F + var newScaleX = 1F + var newScaleY = 1F + + val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + val isLeft = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT) + when (rotation) { + Surface.ROTATION_0 -> when { + isTop && isLeft -> {} + isTop && !isLeft -> { newScaleX = -1F } + !isTop && isLeft -> { newScaleY = -1F } + else /* !isTop && !isLeft */ -> { newRotation = 180F } + } + Surface.ROTATION_90 -> when { + isTop && isLeft -> { newScaleY = -1F } + isTop && !isLeft -> {} + !isTop && isLeft -> { newRotation = 180F } + else /* !isTop && !isLeft */ -> { newScaleX = -1F } + } + Surface.ROTATION_270 -> when { + isTop && isLeft -> { newScaleX = -1F } + isTop && !isLeft -> { newRotation = 180F } + !isTop && isLeft -> {} + else /* !isTop && !isLeft */ -> { newScaleY = -1F } + } + else /* Surface.ROTATION_180 */ -> when { + isTop && isLeft -> { newRotation = 180F } + isTop && !isLeft -> { newScaleY = -1F } + !isTop && isLeft -> { newScaleX = -1F } + else /* !isTop && !isLeft */ -> {} + } + } + + 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 c817f89c7a9bc..256c6751c443b 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt @@ -36,6 +36,8 @@ class RoundedCornerResDelegate( private val density: Float get() = res.displayMetrics.density + private var reloadToken: Int = 0 + var isMultipleRadius: Boolean = false private set @@ -60,12 +62,26 @@ class RoundedCornerResDelegate( reloadMeasures() } - fun reloadAll(newDisplayUniqueId: String?) { - displayUniqueId = newDisplayUniqueId + private fun reloadAll(newReloadToken: Int) { + if (reloadToken == newReloadToken) { + return + } + reloadToken = newReloadToken reloadDrawables() reloadMeasures() } + fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) { + if (displayUniqueId != newDisplayUniqueId) { + displayUniqueId = newDisplayUniqueId + newReloadToken ?.let { reloadToken = it } + reloadDrawables() + reloadMeasures() + } else { + newReloadToken?.let { reloadAll(it) } + } + } + private fun reloadDrawables() { val configIdx = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId) isMultipleRadius = getIsMultipleRadius(configIdx) @@ -85,34 +101,6 @@ class RoundedCornerResDelegate( arrayResId = R.array.config_roundedCornerBottomDrawableArray, backupDrawableId = R.drawable.rounded_corner_bottom ) ?: roundedDrawable - - // If config_roundedCornerMultipleRadius set as true, ScreenDecorations respect the - // (width, height) size of drawable/rounded.xml instead of rounded_corner_radius - if (isMultipleRadius) { - roundedSize = Size( - roundedDrawable?.intrinsicWidth ?: 0, - roundedDrawable?.intrinsicHeight ?: 0) - topRoundedDrawable?.let { - topRoundedSize = Size(it.intrinsicWidth, it.intrinsicHeight) - } - bottomRoundedDrawable?.let { - bottomRoundedSize = Size(it.intrinsicWidth, it.intrinsicHeight) - } - } else { - val defaultRadius = RoundedCorners.getRoundedCornerRadius(res, displayUniqueId) - val topRadius = RoundedCorners.getRoundedCornerTopRadius(res, displayUniqueId) - val bottomRadius = RoundedCorners.getRoundedCornerBottomRadius(res, displayUniqueId) - roundedSize = Size(defaultRadius, defaultRadius) - topRoundedSize = Size(topRadius, topRadius) - bottomRoundedSize = Size(bottomRadius, bottomRadius) - } - - if (topRoundedSize.width == 0) { - topRoundedSize = roundedSize - } - if (bottomRoundedSize.width == 0) { - bottomRoundedSize = roundedSize - } } private fun reloadMeasures(roundedSizeFactor: Int? = null) { @@ -137,8 +125,8 @@ class RoundedCornerResDelegate( bottomRoundedSize = Size(bottomRadius, bottomRadius) } - roundedSizeFactor ?.let { - val length: Int = (it * density).toInt() + if (roundedSizeFactor != null && roundedSizeFactor > 0) { + val length: Int = (roundedSizeFactor * density).toInt() roundedSize = Size(length, length) } @@ -150,7 +138,11 @@ class RoundedCornerResDelegate( } } - fun updateTuningSizeFactor(factor: Int) { + fun updateTuningSizeFactor(factor: Int?, newReloadToken: Int) { + if (reloadToken == newReloadToken) { + return + } + reloadToken = newReloadToken reloadMeasures(factor) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index ec92adb0f48cf..ac78626ff1263 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -33,7 +33,6 @@ import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -61,9 +60,11 @@ import android.util.Size; import android.view.Display; import android.view.DisplayCutout; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowMetrics; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; @@ -102,7 +103,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { private SecureSettings mSecureSettings; private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); private FakeThreadFactory mThreadFactory; - private ArrayList mDecorProviders; + private ArrayList mPrivacyDecorProviders; @Mock private Display mDisplay; @Mock @@ -197,17 +198,43 @@ public class ScreenDecorationsTest extends SysuiTestCase { reset(mTunerService); } + @NonNull + private int[] getRoundCornerIdsFromOverlayId(@DisplayCutout.BoundsPosition int overlayId) { + switch (overlayId) { + case BOUNDS_POSITION_LEFT: + return new int[] { + R.id.rounded_corner_top_left, + R.id.rounded_corner_top_left }; + case BOUNDS_POSITION_TOP: + return new int[] { + R.id.rounded_corner_top_left, + R.id.rounded_corner_top_right }; + case BOUNDS_POSITION_RIGHT: + return new int[] { + R.id.rounded_corner_top_right, + R.id.rounded_corner_bottom_right }; + case BOUNDS_POSITION_BOTTOM: + return new int[] { + R.id.rounded_corner_bottom_left, + R.id.rounded_corner_bottom_right }; + default: + throw new IllegalArgumentException("unknown overlayId: " + overlayId); + } + } - private void verifyRoundedCornerViewsVisibility( + private void verifyRoundedCornerViewsExist( @DisplayCutout.BoundsPosition final int overlayId, - @View.Visibility final int visibility) { + @View.Visibility final boolean isExist) { final View overlay = mScreenDecorations.mOverlays[overlayId].getRootView(); - final View left = overlay.findViewById(R.id.left); - final View right = overlay.findViewById(R.id.right); - assertNotNull(left); - assertNotNull(right); - assertThat(left.getVisibility()).isEqualTo(visibility); - assertThat(right.getVisibility()).isEqualTo(visibility); + for (int id: getRoundCornerIdsFromOverlayId(overlayId)) { + final View view = overlay.findViewById(id); + if (isExist) { + assertNotNull(view); + assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); + } else { + assertNull(view); + } + } } @Nullable @@ -351,8 +378,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true); // Rounded corner views shall not exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -380,8 +407,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall not exist verifyDotViewsNullable(true); @@ -408,8 +435,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -449,21 +476,26 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView() - .findViewById(R.id.left); + .findViewById(R.id.rounded_corner_top_left); View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView() - .findViewById(R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, new Size(testTopRadius, testTopRadius)); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, new Size(testTopRadius, testTopRadius)); + .findViewById(R.id.rounded_corner_top_right); + ViewGroup.LayoutParams leftParams = leftRoundedCorner.getLayoutParams(); + ViewGroup.LayoutParams rightParams = rightRoundedCorner.getLayoutParams(); + assertEquals(leftParams.width, testTopRadius); + assertEquals(leftParams.height, testTopRadius); + assertEquals(rightParams.width, testTopRadius); + assertEquals(rightParams.height, testTopRadius); + leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView() - .findViewById(R.id.left); + .findViewById(R.id.rounded_corner_bottom_left); rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView() - .findViewById(R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, new Size(testBottomRadius, testBottomRadius)); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, new Size(testBottomRadius, testBottomRadius)); + .findViewById(R.id.rounded_corner_bottom_right); + leftParams = leftRoundedCorner.getLayoutParams(); + rightParams = rightRoundedCorner.getLayoutParams(); + assertEquals(leftParams.width, testBottomRadius); + assertEquals(leftParams.height, testBottomRadius); + assertEquals(rightParams.width, testBottomRadius); + assertEquals(rightParams.height, testBottomRadius); } @Test @@ -479,31 +511,27 @@ public class ScreenDecorationsTest extends SysuiTestCase { .when(mScreenDecorations).getCutout(); mScreenDecorations.start(); - final Size topRadius = new Size(testTopRadius, testTopRadius); - final Size bottomRadius = new Size(testBottomRadius, testBottomRadius); - View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() - .findViewById(R.id.left); - boolean isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.left); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius); + View topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() + .findViewById(R.id.rounded_corner_top_left); + View bottomRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() + .findViewById(R.id.rounded_corner_bottom_left); + ViewGroup.LayoutParams topParams = topRoundedCorner.getLayoutParams(); + ViewGroup.LayoutParams bottomParams = bottomRoundedCorner.getLayoutParams(); + assertEquals(topParams.width, testTopRadius); + assertEquals(topParams.height, testTopRadius); + assertEquals(bottomParams.width, testBottomRadius); + assertEquals(bottomParams.height, testBottomRadius); - View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() - .findViewById(R.id.right); - isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius); - - leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() - .findViewById(R.id.left); - isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.left); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius); - - rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() - .findViewById(R.id.right); - isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius); + topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() + .findViewById(R.id.rounded_corner_top_right); + bottomRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() + .findViewById(R.id.rounded_corner_bottom_right); + topParams = topRoundedCorner.getLayoutParams(); + bottomParams = bottomRoundedCorner.getLayoutParams(); + assertEquals(topParams.width, testTopRadius); + assertEquals(topParams.height, testTopRadius); + assertEquals(bottomParams.width, testBottomRadius); + assertEquals(bottomParams.height, testBottomRadius); } @Test @@ -523,8 +551,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall not exist verifyDotViewsNullable(true); @@ -557,8 +585,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -617,10 +645,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Top rounded corner views shall exist because of cutout // but be gone because of no rounded corner - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); // Bottom rounded corner views shall exist because of privacy dot // but be gone because of no rounded corner - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -648,7 +676,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Left rounded corner views shall exist because of cutout // but be gone because of no rounded corner - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_LEFT, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_LEFT, false); // Top privacy dots shall not exist because of no privacy verifyDotViewsNullable(true); @@ -700,8 +728,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Top privacy dots shall not exist because of no privacy dot verifyDotViewsNullable(true); @@ -728,8 +756,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Top privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -859,7 +887,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(true, false, true, false); // Verify each privacy dot id appears only once - mDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> { + mPrivacyDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> { int findCount = 0; for (OverlayWindow overlay: mScreenDecorations.mOverlays) { if (overlay == null) { @@ -913,8 +941,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Both top and bottom windows should be added because of privacy dot, // but their visibility shall be gone because of no rounding. verifyOverlaysExistAndAdded(false, true, false, true); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); when(mContext.getResources().getBoolean( com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout)) @@ -925,8 +953,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Both top and bottom windows should be added because of privacy dot, // but their visibility shall be gone because of no rounding. verifyOverlaysExistAndAdded(false, true, false, true); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); } @Test @@ -1174,14 +1202,14 @@ public class ScreenDecorationsTest extends SysuiTestCase { mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout); - mDecorProviders = new ArrayList<>(); + mPrivacyDecorProviders = new ArrayList<>(); if (privacyDot) { - mDecorProviders.add(mPrivacyDotTopLeftDecorProvider); - mDecorProviders.add(mPrivacyDotTopRightDecorProvider); - mDecorProviders.add(mPrivacyDotBottomLeftDecorProvider); - mDecorProviders.add(mPrivacyDotBottomRightDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotTopLeftDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotTopRightDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotBottomLeftDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotBottomRightDecorProvider); } - when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mDecorProviders); + when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mPrivacyDecorProviders); when(mPrivacyDotDecorProviderFactory.getHasProviders()).thenReturn(privacyDot); } 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 ca74df0a23c59..5182210b95679 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt @@ -19,25 +19,19 @@ package com.android.systemui.decor import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.DisplayCutout -import android.view.LayoutInflater import android.view.Surface import android.view.View -import android.view.ViewGroup import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase -import com.android.systemui.util.mockito.eq 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 -import org.mockito.Mockito.anyInt +import org.mockito.Mockito.never import org.mockito.Mockito.spy +import org.mockito.Mockito.times import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations -import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) @@ -45,62 +39,88 @@ import org.mockito.Mockito.`when` as whenever class OverlayWindowTest : SysuiTestCase() { companion object { - private val TEST_DECOR_VIEW_ID = R.id.privacy_dot_bottom_right_container - private val TEST_DECOR_LAYOUT_ID = R.layout.privacy_dot_bottom_right + private val TEST_DECOR_VIEW_ID_1 = R.id.privacy_dot_top_left_container + private val TEST_DECOR_VIEW_ID_2 = R.id.privacy_dot_bottom_right_container } private lateinit var overlay: OverlayWindow - - @Mock private lateinit var layoutInflater: LayoutInflater - @Mock private lateinit var decorProvider: DecorProvider + private lateinit var decorProvider1: DecorProvider + private lateinit var decorProvider2: DecorProvider @Before fun setUp() { - MockitoAnnotations.initMocks(this) + 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( + viewId = TEST_DECOR_VIEW_ID_2, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + layoutId = R.layout.privacy_dot_bottom_right)) - layoutInflater = spy(LayoutInflater.from(mContext)) - - overlay = OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT) - - whenever(decorProvider.viewId).thenReturn(TEST_DECOR_VIEW_ID) - whenever(decorProvider.inflateView( - eq(layoutInflater), - eq(overlay.rootView), - anyInt()) - ).then { - val layoutInflater = it.getArgument(0) - val parent = it.getArgument(1) - layoutInflater.inflate(TEST_DECOR_LAYOUT_ID, parent) - return@then parent.getChildAt(parent.childCount - 1) - } - } - - @Test - fun testAnyBoundsPositionShallNoExceptionForConstructor() { - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_LEFT) - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_TOP) - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT) - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_BOTTOM) + overlay = OverlayWindow(mContext) } @Test fun testAddProvider() { @Surface.Rotation val rotation = Surface.ROTATION_270 - overlay.addDecorProvider(decorProvider, rotation) - verify(decorProvider, Mockito.times(1)).inflateView( - eq(layoutInflater), eq(overlay.rootView), eq(rotation)) - val viewFoundFromRootView = overlay.rootView.findViewById(TEST_DECOR_VIEW_ID) - Assert.assertNotNull(viewFoundFromRootView) - Assert.assertEquals(viewFoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID)) + overlay.addDecorProvider(decorProvider1, rotation) + overlay.addDecorProvider(decorProvider2, rotation) + + verify(decorProvider1, times(1)).inflateView( + mContext, overlay.rootView, rotation) + verify(decorProvider2, times(1)).inflateView( + mContext, overlay.rootView, rotation) + + val view1FoundFromRootView = overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_1) + Assert.assertNotNull(view1FoundFromRootView) + Assert.assertEquals(view1FoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID_1)) + val view2FoundFromRootView = overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_2) + Assert.assertNotNull(view2FoundFromRootView) + Assert.assertEquals(view2FoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID_2)) } @Test fun testRemoveView() { - @Surface.Rotation val rotation = Surface.ROTATION_270 - overlay.addDecorProvider(decorProvider, rotation) - overlay.removeView(TEST_DECOR_VIEW_ID) - val viewFoundFromRootView = overlay.rootView.findViewById(TEST_DECOR_VIEW_ID) + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270) + overlay.removeView(TEST_DECOR_VIEW_ID_1) + + val viewFoundFromRootView = overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_1) Assert.assertNull(viewFoundFromRootView) - Assert.assertNull(overlay.getView(TEST_DECOR_LAYOUT_ID)) + Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_1)) + } + + @Test + fun testOnReloadResAndMeasureWithoutIds() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + + overlay.onReloadResAndMeasure( + reloadToken = 1, + rotation = Surface.ROTATION_90, + displayUniqueId = null) + verify(decorProvider1, times(1)).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null) + verify(decorProvider2, times(1)).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null) + } + + @Test + fun testOnReloadResAndMeasureWithIds() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + + overlay.onReloadResAndMeasure( + filterIds = arrayOf(TEST_DECOR_VIEW_ID_2), + reloadToken = 1, + rotation = Surface.ROTATION_90, + displayUniqueId = null) + verify(decorProvider1, never()).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null) + verify(decorProvider2, times(1)).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null) } } \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt index bac08176d2ebc..171b76748d26d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.decor import android.content.res.Resources import android.testing.AndroidTestingRunner -import android.testing.TestableLooper.RunWithLooper import android.view.DisplayCutout import androidx.test.filters.SmallTest import com.android.systemui.R @@ -32,7 +31,6 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) -@RunWithLooper(setAsMainLooper = true) @SmallTest class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() { private lateinit var mPrivacyDotDecorProviderFactory: PrivacyDotDecorProviderFactory diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt new file mode 100644 index 0000000000000..621bcf69bb038 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt @@ -0,0 +1,142 @@ +/* + * 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.testing.AndroidTestingRunner +import android.util.Size +import android.view.DisplayCutout +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +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 +import org.mockito.Mockito.spy + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() { + + @Mock private lateinit var roundedCornerResDelegate: RoundedCornerResDelegate + private lateinit var roundedCornerDecorProviderFactory: RoundedCornerDecorProviderFactory + + @Before + fun setUp() { + roundedCornerResDelegate = spy(RoundedCornerResDelegate(mContext.resources, null)) + } + + @Test + fun testNoRoundedCorners() { + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(false, roundedCornerDecorProviderFactory.hasProviders) + Assert.assertEquals(0, roundedCornerDecorProviderFactory.providers.size) + } + + @Test + fun testHasRoundedCornersIfTopWidthLargerThan0() { + Mockito.doReturn(Size(1, 0)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders) + roundedCornerDecorProviderFactory.providers.let { providers -> + Assert.assertEquals(2, providers.size) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + } + } + + @Test + fun testHasRoundedCornersIfBottomWidthLargerThan0() { + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(1, 1)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders) + roundedCornerDecorProviderFactory.providers.let { providers -> + Assert.assertEquals(2, providers.size) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + } + } + + @Test + fun test4CornerDecorProvidersInfo() { + Mockito.doReturn(Size(10, 10)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(10, 10)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(true).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders) + roundedCornerDecorProviderFactory.providers.let { providers -> + Assert.assertEquals(4, providers.size) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + } + } +} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt index b536bfdb944eb..fb6ff76a4e381 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt @@ -45,7 +45,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { } @Test - fun testReloadAllAndDefaultRadius() { + fun testUpdateDisplayUniqueId() { mContext.orCreateTestableResources.addOverrides( mockTypeArray = mockTypedArray, radius = 3, @@ -65,7 +65,34 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { radiusTop = 6, radiusBottom = 0) - roundedCornerResDelegate.reloadAll("test") + roundedCornerResDelegate.updateDisplayUniqueId("test", null) + + assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize) + assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize) + } + + @Test + fun testNotUpdateDisplayUniqueIdButChangeRefreshToken() { + mContext.orCreateTestableResources.addOverrides( + mockTypeArray = mockTypedArray, + radius = 3, + radiusTop = 0, + radiusBottom = 4, + multipleRadius = false) + + roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + + assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize) + assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize) + assertEquals(false, roundedCornerResDelegate.isMultipleRadius) + + mContext.orCreateTestableResources.addOverrides( + mockTypeArray = mockTypedArray, + radius = 5, + radiusTop = 6, + radiusBottom = 0) + + roundedCornerResDelegate.updateDisplayUniqueId(null, 1) assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize) @@ -82,11 +109,21 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) val factor = 5 - roundedCornerResDelegate.updateTuningSizeFactor(factor) + roundedCornerResDelegate.updateTuningSizeFactor(factor, 1) val length = (factor * mContext.resources.displayMetrics.density).toInt() assertEquals(Size(length, length), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(length, length), roundedCornerResDelegate.bottomRoundedSize) + + mContext.orCreateTestableResources.addOverrides( + mockTypeArray = mockTypedArray, + radiusTop = 1, + radiusBottom = 2, + multipleRadius = false) + roundedCornerResDelegate.updateTuningSizeFactor(null, 2) + + assertEquals(Size(1, 1), roundedCornerResDelegate.topRoundedSize) + assertEquals(Size(2, 2), roundedCornerResDelegate.bottomRoundedSize) } @Test