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
This commit is contained in:
Milton Wu
2022-03-10 16:01:24 +08:00
parent 48b334a442
commit cb4664745c
16 changed files with 798 additions and 470 deletions

View File

@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
** Copyright 2020, 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.
-->
<com.android.systemui.RegionInterceptingFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rounded_corners_bottom"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/left"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_gravity="left|bottom"
android:tint="#ff000000"
android:visibility="gone"
android:src="@drawable/rounded_corner_bottom"/>
<ImageView
android:id="@+id/right"
android:layout_width="12dp"
android:layout_height="12dp"
android:tint="#ff000000"
android:visibility="gone"
android:layout_gravity="right|bottom"
android:src="@drawable/rounded_corner_bottom"/>
</com.android.systemui.RegionInterceptingFrameLayout>

View File

@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
** Copyright 2020, 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.
-->
<com.android.systemui.RegionInterceptingFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rounded_corners_top"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/left"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_gravity="left|top"
android:tint="#ff000000"
android:visibility="gone"
android:src="@drawable/rounded_corner_top"/>
<ImageView
android:id="@+id/right"
android:layout_width="12dp"
android:layout_height="12dp"
android:tint="#ff000000"
android:visibility="gone"
android:layout_gravity="right|top"
android:src="@drawable/rounded_corner_top"/>
</com.android.systemui.RegionInterceptingFrameLayout>

View File

@@ -167,5 +167,11 @@
<item type="id" name="action_move_bottom_right"/>
<item type="id" name="action_move_to_edge_and_hide"/>
<item type="id" name="action_move_out_edge_and_show"/>
<!-- rounded corner view id -->
<item type="id" name="rounded_corner_top_left"/>
<item type="id" name="rounded_corner_top_right"/>
<item type="id" name="rounded_corner_bottom_left"/>
<item type="id" name="rounded_corner_bottom_right"/>
</resources>

View File

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

View File

@@ -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<DecorProvider> decorProviders = mDotFactory.getProviders();
if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) {
if (hasRoundedCorners() || shouldDrawCutout() || isPrivacyDotEnabled()) {
List<DecorProvider> 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<DecorProvider>, List<DecorProvider>> 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<DecorProvider> 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<DecorProvider> 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();

View File

@@ -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<Int>
/**
* 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

View File

@@ -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<Int, Pair<View, DecorProvider>>()
val rootView = layoutInflater.inflate(layoutId, null) as ViewGroup
private val viewProviderMap: MutableMap<Int, Pair<View, DecorProvider>> = 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<Int>? = 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)
}
}
}
}

View File

@@ -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 */)
}
}

View File

@@ -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<DecorProvider>
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()
}
}
}

View File

@@ -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<Int>, @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
}

View File

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

View File

@@ -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<DecorProvider> mDecorProviders;
private ArrayList<DecorProvider> 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);
}

View File

@@ -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<LayoutInflater>(0)
val parent = it.getArgument<ViewGroup>(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<View>(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<View>(TEST_DECOR_VIEW_ID_1)
Assert.assertNotNull(view1FoundFromRootView)
Assert.assertEquals(view1FoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID_1))
val view2FoundFromRootView = overlay.rootView.findViewById<View>(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<View>(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<View>(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)
}
}

View File

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

View File

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

View File

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