ScreenDecorations Cutout Provider
Move cutout to provider.
Bug: 232874879
Test: atest ScreenDecorationsTest CutoutDecorProviderFactoryTest \
OverlayWindowTest
Test: fold/unfold/rotate/debugRoundedCornerSize/debugFlag on sw-layer
devices
Test: adb shell dumpsys activity service com.android.systemui
Change-Id: Iadda7085349ed02a84c5e7501034872877f460c8
This commit is contained in:
@@ -132,6 +132,9 @@
|
||||
<item type="id" name="status_bar_view_state_tag" />
|
||||
|
||||
<item type="id" name="display_cutout" />
|
||||
<item type="id" name="display_cutout_left" />
|
||||
<item type="id" name="display_cutout_right" />
|
||||
<item type="id" name="display_cutout_bottom" />
|
||||
|
||||
<item type="id" name="row_tag_for_content_view" />
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.graphics.drawable.Drawable;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.graphics.common.AlphaInterpretation;
|
||||
import android.hardware.graphics.common.DisplayDecorationSupport;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.Trace;
|
||||
@@ -72,6 +73,7 @@ import com.android.settingslib.Utils;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.decor.CutoutDecorProviderFactory;
|
||||
import com.android.systemui.decor.DecorProvider;
|
||||
import com.android.systemui.decor.DecorProviderFactory;
|
||||
import com.android.systemui.decor.DecorProviderKt;
|
||||
@@ -139,13 +141,11 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
protected RoundedCornerResDelegate mRoundedCornerResDelegate;
|
||||
@VisibleForTesting
|
||||
protected DecorProviderFactory mRoundedCornerFactory;
|
||||
private CutoutDecorProviderFactory mCutoutFactory;
|
||||
private int mProviderRefreshToken = 0;
|
||||
@VisibleForTesting
|
||||
protected OverlayWindow[] mOverlays = null;
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
DisplayCutoutView[] mCutoutViews;
|
||||
@VisibleForTesting
|
||||
ViewGroup mScreenDecorHwcWindow;
|
||||
@VisibleForTesting
|
||||
ScreenDecorHwcLayer mScreenDecorHwcLayer;
|
||||
@@ -187,17 +187,28 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCutoutViews == null) {
|
||||
Log.w(TAG, "DisplayCutoutView not initialized onApplyCameraProtection");
|
||||
return;
|
||||
final int[] ids = {
|
||||
R.id.display_cutout,
|
||||
R.id.display_cutout_left,
|
||||
R.id.display_cutout_right,
|
||||
R.id.display_cutout_bottom
|
||||
};
|
||||
int setProtectionCnt = 0;
|
||||
for (int id: ids) {
|
||||
final View view = getOverlayView(id);
|
||||
if (!(view instanceof DisplayCutoutView)) {
|
||||
continue;
|
||||
}
|
||||
++setProtectionCnt;
|
||||
final DisplayCutoutView dcv = (DisplayCutoutView) view;
|
||||
dcv.setProtection(protectionPath, bounds);
|
||||
dcv.enableShowProtection(true);
|
||||
}
|
||||
|
||||
// Show the extra protection around the front facing camera if necessary
|
||||
for (DisplayCutoutView dcv : mCutoutViews) {
|
||||
// Check Null since not all mCutoutViews[pos] be inflated at the meanwhile
|
||||
if (dcv != null) {
|
||||
dcv.setProtection(protectionPath, bounds);
|
||||
dcv.enableShowProtection(true);
|
||||
if (setProtectionCnt == 0) {
|
||||
if (Build.isDebuggable()) {
|
||||
throw new RuntimeException("CutoutView not initialized showCameraProtection");
|
||||
} else {
|
||||
Log.e(TAG, "CutoutView not initialized showCameraProtection");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,15 +230,26 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCutoutViews == null) {
|
||||
Log.w(TAG, "DisplayCutoutView not initialized onHideCameraProtection");
|
||||
return;
|
||||
final int[] ids = {
|
||||
R.id.display_cutout,
|
||||
R.id.display_cutout_left,
|
||||
R.id.display_cutout_right,
|
||||
R.id.display_cutout_bottom
|
||||
};
|
||||
int setProtectionCnt = 0;
|
||||
for (int id: ids) {
|
||||
final View view = getOverlayView(id);
|
||||
if (!(view instanceof DisplayCutoutView)) {
|
||||
continue;
|
||||
}
|
||||
++setProtectionCnt;
|
||||
((DisplayCutoutView) view).enableShowProtection(false);
|
||||
}
|
||||
// Go back to the regular anti-aliasing
|
||||
for (DisplayCutoutView dcv : mCutoutViews) {
|
||||
// Check Null since not all mCutoutViews[pos] be inflated at the meanwhile
|
||||
if (dcv != null) {
|
||||
dcv.enableShowProtection(false);
|
||||
if (setProtectionCnt == 0) {
|
||||
if (Build.isDebuggable()) {
|
||||
throw new RuntimeException("CutoutView not initialized hideCameraProtection");
|
||||
} else {
|
||||
Log.e(TAG, "CutoutView not initialized hideCameraProtection");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,6 +357,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
decorProviders.addAll(mFaceScanningFactory.getProviders());
|
||||
if (!hasHwLayer) {
|
||||
decorProviders.addAll(mRoundedCornerFactory.getProviders());
|
||||
decorProviders.addAll(mCutoutFactory.getProviders());
|
||||
}
|
||||
return decorProviders;
|
||||
}
|
||||
@@ -379,6 +402,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(
|
||||
getPhysicalPixelDisplaySizeRatio());
|
||||
mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate);
|
||||
mCutoutFactory = getCutoutFactory();
|
||||
mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport();
|
||||
updateHwLayerRoundedCornerDrawable();
|
||||
setupDecorations();
|
||||
@@ -483,18 +507,13 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
if (needToUpdateProviderViews) {
|
||||
updateOverlayProviderViews(null);
|
||||
} else {
|
||||
updateOverlayProviderViews(new Integer[] { mFaceScanningViewId });
|
||||
}
|
||||
|
||||
if (mCutoutViews != null) {
|
||||
final int size = mCutoutViews.length;
|
||||
for (int i = 0; i < size; i++) {
|
||||
final DisplayCutoutView cutoutView = mCutoutViews[i];
|
||||
if (cutoutView == null) {
|
||||
continue;
|
||||
}
|
||||
cutoutView.onDisplayChanged(newUniqueId);
|
||||
}
|
||||
updateOverlayProviderViews(new Integer[] {
|
||||
mFaceScanningViewId,
|
||||
R.id.display_cutout,
|
||||
R.id.display_cutout_left,
|
||||
R.id.display_cutout_right,
|
||||
R.id.display_cutout_bottom,
|
||||
});
|
||||
}
|
||||
|
||||
if (mScreenDecorHwcLayer != null) {
|
||||
@@ -507,8 +526,9 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
updateConfiguration();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
private View getOverlayView(@IdRes int id) {
|
||||
View getOverlayView(@IdRes int id) {
|
||||
if (mOverlays == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -565,18 +585,18 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
removeHwcOverlay();
|
||||
}
|
||||
|
||||
final DisplayCutout cutout = getCutout();
|
||||
boolean[] hasCreatedOverlay = new boolean[BOUNDS_POSITION_LENGTH];
|
||||
final boolean shouldOptimizeVisibility = shouldOptimizeVisibility();
|
||||
Integer bound;
|
||||
while ((bound = DecorProviderKt.getProperBound(decorProviders)) != null) {
|
||||
hasCreatedOverlay[bound] = true;
|
||||
Pair<List<DecorProvider>, List<DecorProvider>> pair =
|
||||
DecorProviderKt.partitionAlignedBound(decorProviders, bound);
|
||||
decorProviders = pair.getSecond();
|
||||
createOverlay(bound, pair.getFirst(), shouldOptimizeVisibility);
|
||||
}
|
||||
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
|
||||
if (shouldShowSwLayerCutout(i, cutout)
|
||||
|| shouldShowSwLayerFaceScan(i, cutout)
|
||||
|| shouldShowSwLayerRoundedCorner(i, cutout)
|
||||
|| shouldShowSwLayerPrivacyDot(i, cutout)) {
|
||||
Pair<List<DecorProvider>, List<DecorProvider>> pair =
|
||||
DecorProviderKt.partitionAlignedBound(decorProviders, i);
|
||||
decorProviders = pair.getSecond();
|
||||
createOverlay(i, pair.getFirst(), shouldOptimizeVisibility);
|
||||
} else {
|
||||
if (!hasCreatedOverlay[i]) {
|
||||
removeOverlay(i);
|
||||
}
|
||||
}
|
||||
@@ -639,9 +659,10 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
DisplayCutout getCutout() {
|
||||
return mContext.getDisplay().getCutout();
|
||||
// For unit test to override
|
||||
protected CutoutDecorProviderFactory getCutoutFactory() {
|
||||
return new CutoutDecorProviderFactory(mContext.getResources(),
|
||||
mContext.getDisplay());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -731,16 +752,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
overlayView.setAlpha(0);
|
||||
overlayView.setForceDarkAllowed(false);
|
||||
|
||||
// Only show cutout in mOverlays when hwc doesn't support screen decoration
|
||||
if (mHwcScreenDecorationSupport == null) {
|
||||
if (mCutoutViews == null) {
|
||||
mCutoutViews = new DisplayCutoutView[BOUNDS_POSITION_LENGTH];
|
||||
}
|
||||
mCutoutViews[pos] = new DisplayCutoutView(mContext, pos);
|
||||
overlayView.addView(mCutoutViews[pos]);
|
||||
mCutoutViews[pos].updateRotation(mRotation);
|
||||
}
|
||||
|
||||
mWindowManager.addView(overlayView, getWindowLayoutParams(pos));
|
||||
|
||||
overlayView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
|
||||
@@ -947,27 +958,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
mTintColor = Color.RED;
|
||||
}
|
||||
|
||||
if (mOverlays == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
|
||||
if (mOverlays[i] == null) {
|
||||
continue;
|
||||
}
|
||||
final ViewGroup overlayView = mOverlays[i].getRootView();
|
||||
final int size = overlayView.getChildCount();
|
||||
View child;
|
||||
for (int j = 0; j < size; j++) {
|
||||
child = overlayView.getChildAt(j);
|
||||
if (child instanceof DisplayCutoutView && child.getId() == R.id.display_cutout) {
|
||||
((DisplayCutoutView) child).setColor(mTintColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateOverlayProviderViews(new Integer[] {
|
||||
mFaceScanningViewId,
|
||||
R.id.display_cutout,
|
||||
R.id.display_cutout_left,
|
||||
R.id.display_cutout_right,
|
||||
R.id.display_cutout_bottom,
|
||||
R.id.rounded_corner_top_left,
|
||||
R.id.rounded_corner_top_right,
|
||||
R.id.rounded_corner_bottom_left,
|
||||
@@ -1092,15 +1088,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
updateHwLayerRoundedCornerDrawable();
|
||||
}
|
||||
updateLayoutParams();
|
||||
// update cutout view rotation
|
||||
if (mCutoutViews != null) {
|
||||
for (final DisplayCutoutView cutoutView: mCutoutViews) {
|
||||
if (cutoutView == null) {
|
||||
continue;
|
||||
}
|
||||
cutoutView.updateRotation(mRotation);
|
||||
}
|
||||
}
|
||||
|
||||
// update all provider views inside overlay
|
||||
updateOverlayProviderViews(null);
|
||||
@@ -1119,46 +1106,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
return mRoundedCornerFactory.getHasProviders();
|
||||
}
|
||||
|
||||
private boolean isDefaultShownOverlayPos(@BoundsPosition int pos,
|
||||
@Nullable DisplayCutout cutout) {
|
||||
// for cutout is null or cutout with only waterfall.
|
||||
final boolean emptyBoundsOrWaterfall = cutout == null || cutout.isBoundsEmpty();
|
||||
// Shows rounded corner on left and right overlays only when there is no top or bottom
|
||||
// cutout.
|
||||
final int rotatedTop = getBoundPositionFromRotation(BOUNDS_POSITION_TOP, mRotation);
|
||||
final int rotatedBottom = getBoundPositionFromRotation(BOUNDS_POSITION_BOTTOM, mRotation);
|
||||
if (emptyBoundsOrWaterfall || !cutout.getBoundingRectsAll()[rotatedTop].isEmpty()
|
||||
|| !cutout.getBoundingRectsAll()[rotatedBottom].isEmpty()) {
|
||||
return pos == BOUNDS_POSITION_TOP || pos == BOUNDS_POSITION_BOTTOM;
|
||||
} else {
|
||||
return pos == BOUNDS_POSITION_LEFT || pos == BOUNDS_POSITION_RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldShowSwLayerRoundedCorner(@BoundsPosition int pos,
|
||||
@Nullable DisplayCutout cutout) {
|
||||
return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout)
|
||||
&& mHwcScreenDecorationSupport == null;
|
||||
}
|
||||
|
||||
private boolean shouldShowSwLayerPrivacyDot(@BoundsPosition int pos,
|
||||
@Nullable DisplayCutout cutout) {
|
||||
return isPrivacyDotEnabled() && isDefaultShownOverlayPos(pos, cutout);
|
||||
}
|
||||
|
||||
private boolean shouldShowSwLayerFaceScan(@BoundsPosition int pos,
|
||||
@Nullable DisplayCutout cutout) {
|
||||
return mFaceScanningFactory.getHasProviders() && isDefaultShownOverlayPos(pos, cutout);
|
||||
}
|
||||
|
||||
private boolean shouldShowSwLayerCutout(@BoundsPosition int pos,
|
||||
@Nullable DisplayCutout cutout) {
|
||||
final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
|
||||
final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
|
||||
return (bounds != null && !bounds[rotatedPos].isEmpty()
|
||||
&& mHwcScreenDecorationSupport == null);
|
||||
}
|
||||
|
||||
private boolean shouldOptimizeVisibility() {
|
||||
return (isPrivacyDotEnabled() || mFaceScanningFactory.getHasProviders())
|
||||
&& (mHwcScreenDecorationSupport != null
|
||||
@@ -1167,7 +1114,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
}
|
||||
|
||||
private boolean shouldDrawCutout() {
|
||||
return shouldDrawCutout(mContext);
|
||||
return mCutoutFactory.getHasProviders();
|
||||
}
|
||||
|
||||
static boolean shouldDrawCutout(Context context) {
|
||||
@@ -1283,7 +1230,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
|
||||
paint.setColor(mColor);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
setId(R.id.display_cutout);
|
||||
if (DEBUG) {
|
||||
getViewTreeObserver().addOnDrawListener(() -> Log.i(TAG,
|
||||
getWindowTitleByPos(pos) + " drawn in rot " + mRotation));
|
||||
@@ -1291,6 +1237,9 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
if (color == mColor) {
|
||||
return;
|
||||
}
|
||||
mColor = color;
|
||||
paint.setColor(mColor);
|
||||
invalidate();
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.decor
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.util.Log
|
||||
import android.view.Display
|
||||
import android.view.DisplayCutout
|
||||
import android.view.DisplayInfo
|
||||
|
||||
class CutoutDecorProviderFactory constructor(
|
||||
private val res: Resources,
|
||||
private val display: Display?,
|
||||
) : DecorProviderFactory() {
|
||||
|
||||
val displayInfo = DisplayInfo()
|
||||
|
||||
override val hasProviders: Boolean
|
||||
get() {
|
||||
display?.getDisplayInfo(displayInfo) ?: run {
|
||||
Log.w(TAG, "display is null, can't update displayInfo")
|
||||
}
|
||||
return DisplayCutout.getFillBuiltInDisplayCutout(res, displayInfo.uniqueId)
|
||||
}
|
||||
|
||||
override val providers: List<DecorProvider>
|
||||
get() {
|
||||
if (!hasProviders) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
return ArrayList<DecorProvider>().also { list ->
|
||||
// We need to update displayInfo before using it, but it has already updated during
|
||||
// accessing hasProviders field
|
||||
displayInfo.displayCutout?.getBoundBaseOnCurrentRotation()?.let { bounds ->
|
||||
for (bound in bounds) {
|
||||
list.add(
|
||||
CutoutDecorProviderImpl(bound.baseOnRotation0(displayInfo.rotation))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val TAG = "CutoutDecorProviderFactory"
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.decor
|
||||
|
||||
import android.content.Context
|
||||
import android.view.DisplayCutout
|
||||
import android.view.Surface
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.ScreenDecorations.DisplayCutoutView
|
||||
|
||||
class CutoutDecorProviderImpl(
|
||||
@DisplayCutout.BoundsPosition override val alignedBound: Int
|
||||
) : BoundDecorProvider() {
|
||||
|
||||
override val viewId: Int = when (alignedBound) {
|
||||
DisplayCutout.BOUNDS_POSITION_TOP -> R.id.display_cutout
|
||||
DisplayCutout.BOUNDS_POSITION_LEFT -> R.id.display_cutout_left
|
||||
DisplayCutout.BOUNDS_POSITION_RIGHT -> R.id.display_cutout_right
|
||||
else -> R.id.display_cutout_bottom
|
||||
}
|
||||
|
||||
override fun inflateView(
|
||||
context: Context,
|
||||
parent: ViewGroup,
|
||||
@Surface.Rotation rotation: Int,
|
||||
tintColor: Int
|
||||
): View {
|
||||
return DisplayCutoutView(context, alignedBound).also { view ->
|
||||
view.id = viewId
|
||||
view.setColor(tintColor)
|
||||
parent.addView(view)
|
||||
view.updateRotation(rotation)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReloadResAndMeasure(
|
||||
view: View,
|
||||
reloadToken: Int,
|
||||
@Surface.Rotation rotation: Int,
|
||||
tintColor: Int,
|
||||
displayUniqueId: String?
|
||||
) {
|
||||
(view as? DisplayCutoutView)?.let { cutoutView ->
|
||||
cutoutView.setColor(tintColor)
|
||||
cutoutView.updateRotation(rotation)
|
||||
cutoutView.onDisplayChanged(displayUniqueId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ abstract class DecorProvider {
|
||||
abstract val viewId: Int
|
||||
|
||||
/** The number of total aligned bounds */
|
||||
val numOfAlignedEdge: Int
|
||||
val numOfAlignedBound: Int
|
||||
get() = alignedBounds.size
|
||||
|
||||
/** The aligned bounds for the view which is created through inflateView() */
|
||||
@@ -57,16 +57,8 @@ abstract class DecorProvider {
|
||||
@Surface.Rotation rotation: Int,
|
||||
tintColor: Int
|
||||
): View
|
||||
}
|
||||
|
||||
/**
|
||||
* Split list to 2 list, and return it back as Pair<>. The providers on the first list contains this
|
||||
* alignedBound element. The providers on the second list do not contain this alignedBound element
|
||||
*/
|
||||
fun List<DecorProvider>.partitionAlignedBound(
|
||||
@DisplayCutout.BoundsPosition alignedBound: Int
|
||||
): Pair<List<DecorProvider>, List<DecorProvider>> {
|
||||
return partition { it.alignedBounds.contains(alignedBound) }
|
||||
override fun toString() = "${javaClass.simpleName}{alignedBounds=$alignedBounds}"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,3 +86,60 @@ abstract class BoundDecorProvider : DecorProvider() {
|
||||
listOf(alignedBound)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split list to 2 list, and return it back as Pair<>. The providers on the first list contains this
|
||||
* alignedBound element. The providers on the second list do not contain this alignedBound element
|
||||
*/
|
||||
fun List<DecorProvider>.partitionAlignedBound(
|
||||
@DisplayCutout.BoundsPosition alignedBound: Int
|
||||
): Pair<List<DecorProvider>, List<DecorProvider>> {
|
||||
return partition { it.alignedBounds.contains(alignedBound) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the proper bound from DecorProvider list
|
||||
* Time complexity: O(N), N is the number of providers
|
||||
*
|
||||
* Choose order
|
||||
* 1. Return null if list is empty
|
||||
* 2. If list contains BoundDecorProvider, return its alignedBound[0] because it is a must-have
|
||||
* bound
|
||||
* 3. Return the bound with most DecorProviders
|
||||
*/
|
||||
fun List<DecorProvider>.getProperBound(): Int? {
|
||||
// Return null if list is empty
|
||||
if (isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Choose alignedBounds[0] of BoundDecorProvider if any
|
||||
val singleBoundProvider = firstOrNull { it.numOfAlignedBound == 1 }
|
||||
if (singleBoundProvider != null) {
|
||||
return singleBoundProvider.alignedBounds[0]
|
||||
}
|
||||
|
||||
// Return the bound with most DecorProviders
|
||||
val boundCount = intArrayOf(0, 0, 0, 0)
|
||||
for (provider in this) {
|
||||
for (bound in provider.alignedBounds) {
|
||||
boundCount[bound]++
|
||||
}
|
||||
}
|
||||
var maxCount = 0
|
||||
var maxCountBound: Int? = null
|
||||
val bounds = arrayOf(
|
||||
// Put top and bottom at first to get the highest priority to be chosen
|
||||
DisplayCutout.BOUNDS_POSITION_TOP,
|
||||
DisplayCutout.BOUNDS_POSITION_BOTTOM,
|
||||
DisplayCutout.BOUNDS_POSITION_LEFT,
|
||||
DisplayCutout.BOUNDS_POSITION_RIGHT
|
||||
)
|
||||
for (bound in bounds) {
|
||||
if (boundCount[bound] > maxCount) {
|
||||
maxCountBound = bound
|
||||
maxCount = boundCount[bound]
|
||||
}
|
||||
}
|
||||
return maxCountBound
|
||||
}
|
||||
|
||||
@@ -114,7 +114,8 @@ class OverlayWindow(private val context: Context) {
|
||||
pw.println(" rootView=$rootView")
|
||||
for (i in 0 until rootView.childCount) {
|
||||
val child = rootView.getChildAt(i)
|
||||
pw.println(" child[$i]=$child")
|
||||
val provider = viewProviderMap[child.id]?.second
|
||||
pw.println(" child[$i]=$child $provider")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ package com.android.systemui;
|
||||
|
||||
import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
|
||||
import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
|
||||
import static android.view.DisplayCutout.BOUNDS_POSITION_LENGTH;
|
||||
import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
|
||||
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
|
||||
@@ -36,6 +35,7 @@ import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
@@ -49,7 +49,6 @@ import android.annotation.IdRes;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
@@ -60,7 +59,6 @@ import android.os.Handler;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.util.RotationUtils;
|
||||
import android.util.Size;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayCutout;
|
||||
@@ -80,6 +78,8 @@ import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.systemui.biometrics.AuthController;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.decor.CornerDecorProvider;
|
||||
import com.android.systemui.decor.CutoutDecorProviderFactory;
|
||||
import com.android.systemui.decor.CutoutDecorProviderImpl;
|
||||
import com.android.systemui.decor.DecorProvider;
|
||||
import com.android.systemui.decor.DecorProviderFactory;
|
||||
import com.android.systemui.decor.FaceScanningOverlayProviderImpl;
|
||||
@@ -157,6 +157,9 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private DisplayInfo mDisplayInfo;
|
||||
private PrivacyDotViewController.ShowingListener mPrivacyDotShowingListener;
|
||||
@Mock
|
||||
private CutoutDecorProviderFactory mCutoutFactory;
|
||||
private List<DecorProvider> mMockCutoutList;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@@ -206,6 +209,11 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
DisplayCutout.BOUNDS_POSITION_RIGHT,
|
||||
R.layout.privacy_dot_bottom_right));
|
||||
|
||||
// Default no cutout
|
||||
mMockCutoutList = new ArrayList<>();
|
||||
doAnswer(it -> !(mMockCutoutList.isEmpty())).when(mCutoutFactory).getHasProviders();
|
||||
doReturn(mMockCutoutList).when(mCutoutFactory).getProviders();
|
||||
|
||||
mFaceScanningDecorProvider = spy(new FaceScanningOverlayProviderImpl(
|
||||
BOUNDS_POSITION_TOP,
|
||||
mAuthController,
|
||||
@@ -239,6 +247,11 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
super.updateOverlayWindowVisibilityIfViewExists(view);
|
||||
mExecutor.runAllReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CutoutDecorProviderFactory getCutoutFactory() {
|
||||
return ScreenDecorationsTest.this.mCutoutFactory;
|
||||
}
|
||||
});
|
||||
mScreenDecorations.mDisplayInfo = mDisplayInfo;
|
||||
doReturn(1f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio();
|
||||
@@ -429,11 +442,9 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testNoRounding_NoCutout_NoPrivacyDot_NoFaceScanning() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, false /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// no cutout
|
||||
doReturn(null).when(mScreenDecorations).getCutout();
|
||||
// no cutout (default)
|
||||
|
||||
mScreenDecorations.start();
|
||||
// No views added.
|
||||
@@ -448,11 +459,9 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testNoRounding_NoCutout_PrivacyDot_NoFaceScanning() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// no cutout
|
||||
doReturn(null).when(mScreenDecorations).getCutout();
|
||||
// no cutout (default)
|
||||
|
||||
mScreenDecorations.start();
|
||||
|
||||
@@ -484,11 +493,9 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testRounding_NoCutout_NoPrivacyDot_NoFaceScanning() {
|
||||
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
20 /* roundedPadding */, false /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// no cutout
|
||||
doReturn(null).when(mScreenDecorations).getCutout();
|
||||
// no cutout (default)
|
||||
|
||||
mScreenDecorations.start();
|
||||
|
||||
@@ -516,11 +523,9 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testRounding_NoCutout_PrivacyDot_NoFaceScanning() {
|
||||
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
20 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// no cutout
|
||||
doReturn(null).when(mScreenDecorations).getCutout();
|
||||
// no cutout (default)
|
||||
|
||||
mScreenDecorations.start();
|
||||
|
||||
@@ -555,10 +560,9 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
/* roundedTopDrawable */,
|
||||
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded3px)
|
||||
/* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
// no cutout
|
||||
doReturn(null).when(mScreenDecorations).getCutout();
|
||||
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// no cutout (default)
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Size of corner view should same as rounded_corner_radius{_top|_bottom}
|
||||
@@ -574,11 +578,9 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
/* roundedTopDrawable */,
|
||||
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded3px)
|
||||
/* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// no cutout
|
||||
doReturn(null).when(mScreenDecorations).getCutout();
|
||||
// no cutout (default)
|
||||
|
||||
mScreenDecorations.start();
|
||||
View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
|
||||
@@ -611,13 +613,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
/* roundedTopDrawable */,
|
||||
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded5px)
|
||||
/* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// left cutout
|
||||
final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
|
||||
|
||||
mScreenDecorations.start();
|
||||
View topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
|
||||
@@ -647,13 +646,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testNoRounding_CutoutShortEdge_NoPrivacyDot() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// top cutout
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Top window is created for top cutout.
|
||||
@@ -671,13 +667,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testNoRounding_CutoutShortEdge_PrivacyDot() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// top cutout
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Top window is created for top cutout.
|
||||
@@ -706,13 +699,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testNoRounding_CutoutLongEdge_NoPrivacyDot() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// left cutout
|
||||
final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Left window is created for left cutout.
|
||||
@@ -734,13 +724,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testNoRounding_CutoutLongEdge_PrivacyDot() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// left cutout
|
||||
final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Left window is created for left cutout.
|
||||
@@ -762,13 +749,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testRounding_CutoutShortEdge_NoPrivacyDot() {
|
||||
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
20 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// top cutout
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Top window is created for rounded corner and top cutout.
|
||||
@@ -791,13 +775,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testRounding_CutoutShortEdge_PrivacyDot() {
|
||||
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
20 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// top cutout
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Top window is created for rounded corner and top cutout.
|
||||
@@ -823,13 +804,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testRounding_CutoutLongEdge_NoPrivacyDot() {
|
||||
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
20 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// left cutout
|
||||
final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Left window is created for rounded corner and left cutout.
|
||||
@@ -842,13 +820,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testRounding_CutoutLongEdge_PrivacyDot() {
|
||||
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
20 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// left cutout
|
||||
final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Left window is created for rounded corner, left cutout, and privacy.
|
||||
@@ -863,13 +838,11 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testRounding_CutoutShortAndLongEdge_NoPrivacyDot() {
|
||||
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
20 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// top and left cutout
|
||||
final Rect[] bounds = {new Rect(0, 50, 1, 60), new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(1, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Top window is created for rounded corner and top cutout.
|
||||
@@ -883,13 +856,11 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testRounding_CutoutShortAndLongEdge_PrivacyDot() {
|
||||
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
20 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// top and left cutout
|
||||
final Rect[] bounds = {new Rect(0, 50, 1, 60), new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(1, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Top window is created for rounded corner and top cutout.
|
||||
@@ -905,21 +876,16 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout_NoPrivacyDot() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// Set to short edge cutout(top).
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
verifyOverlaysExistAndAdded(false, true, false, false, View.VISIBLE);
|
||||
|
||||
// Switch to long edge cutout(left).
|
||||
final Rect[] newBounds = {new Rect(0, 50, 1, 60), null, null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), newBounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.set(0, new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
|
||||
|
||||
mScreenDecorations.onConfigurationChanged(new Configuration());
|
||||
verifyOverlaysExistAndAdded(true, false, false, false, View.VISIBLE);
|
||||
@@ -929,13 +895,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout_PrivacyDot() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// Set to short edge cutout(top).
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
|
||||
@@ -943,9 +906,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
verify(mDotViewController, times(1)).setShowingListener(null);
|
||||
|
||||
// Switch to long edge cutout(left).
|
||||
final Rect[] newBounds = {new Rect(0, 50, 1, 60), null, null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), newBounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.set(0, new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
|
||||
|
||||
mScreenDecorations.onConfigurationChanged(new Configuration());
|
||||
verifyOverlaysExistAndAdded(true, false, true, false, View.VISIBLE);
|
||||
@@ -973,20 +934,16 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testDelayedCutout_NoPrivacyDot() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, false /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// top cutout
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
// No cutout (default)
|
||||
|
||||
mScreenDecorations.start();
|
||||
verifyOverlaysExistAndAdded(false, false, false, false, null);
|
||||
|
||||
when(mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout))
|
||||
.thenReturn(true);
|
||||
// top cutout
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.onConfigurationChanged(new Configuration());
|
||||
|
||||
// Only top windows should be added.
|
||||
@@ -997,13 +954,9 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testDelayedCutout_PrivacyDot() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// top cutout
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
// no cutout (default)
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Both top and bottom windows should be added with INVISIBLE because of only privacy dot,
|
||||
@@ -1015,9 +968,9 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
verify(mDotViewController, times(1)).setShowingListener(
|
||||
mScreenDecorations.mPrivacyDotShowingListener);
|
||||
|
||||
when(mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout))
|
||||
.thenReturn(true);
|
||||
// top cutout
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.onConfigurationChanged(new Configuration());
|
||||
|
||||
// Both top and bottom windows should be added with VISIBLE because of privacy dot and
|
||||
@@ -1043,8 +996,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
/* roundedTopDrawable */,
|
||||
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded4px)
|
||||
/* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning*/);
|
||||
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning*/);
|
||||
mDisplayInfo.rotation = Surface.ROTATION_0;
|
||||
|
||||
mScreenDecorations.start();
|
||||
@@ -1058,8 +1010,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
/* roundedTopDrawable */,
|
||||
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded5px)
|
||||
/* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning*/);
|
||||
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning*/);
|
||||
mDisplayInfo.rotation = Surface.ROTATION_270;
|
||||
|
||||
mScreenDecorations.onConfigurationChanged(null);
|
||||
@@ -1072,8 +1023,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testOnlyRoundedCornerRadiusTop() {
|
||||
setupResources(0 /* radius */, 10 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
mScreenDecorations.start();
|
||||
|
||||
@@ -1094,8 +1044,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testOnlyRoundedCornerRadiusBottom() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 20 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
mScreenDecorations.start();
|
||||
|
||||
@@ -1166,13 +1115,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testSupportHwcLayer_SwitchFrom_NotSupport() {
|
||||
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// top cutout
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
// should only inflate mOverlays when the hwc doesn't support screen decoration
|
||||
@@ -1195,16 +1141,13 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testNotSupportHwcLayer_SwitchFrom_Support() {
|
||||
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
|
||||
decorationSupport.format = PixelFormat.R_8;
|
||||
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
|
||||
|
||||
// top cutout
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
// should only inflate hwc layer when the hwc supports screen decoration
|
||||
@@ -1234,16 +1177,13 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
/* roundedTopDrawable */,
|
||||
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded4px)
|
||||
/* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
|
||||
true /* faceScanning */);
|
||||
0 /* roundedPadding */, true /* privacyDot */, true /* faceScanning */);
|
||||
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
|
||||
decorationSupport.format = PixelFormat.R_8;
|
||||
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
|
||||
|
||||
// top cutout
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Inflate top and bottom overlay with INVISIBLE because of only privacy dots on sw layer
|
||||
@@ -1277,11 +1217,9 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testAutoShowHideOverlayWindowWhenNoRoundedAndNoCutout() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */,
|
||||
true /* faceScanning */);
|
||||
0 /* roundedPadding */, true /* privacyDot */, true /* faceScanning */);
|
||||
|
||||
// no cutout
|
||||
doReturn(null).when(mScreenDecorations).getCutout();
|
||||
// no cutout (default)
|
||||
|
||||
mScreenDecorations.start();
|
||||
// Inflate top and bottom overlay with INVISIBLE because of only privacy dots on sw layer
|
||||
@@ -1315,16 +1253,13 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testHwcLayer_noPrivacyDot_noFaceScanning() {
|
||||
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
|
||||
decorationSupport.format = PixelFormat.R_8;
|
||||
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
|
||||
|
||||
// top cutout
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
|
||||
@@ -1337,16 +1272,13 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testHwcLayer_PrivacyDot_FaceScanning() {
|
||||
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
|
||||
true /* faceScanning */);
|
||||
0 /* roundedPadding */, true /* privacyDot */, true /* faceScanning */);
|
||||
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
|
||||
decorationSupport.format = PixelFormat.R_8;
|
||||
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
|
||||
|
||||
// top cutout
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
|
||||
@@ -1364,16 +1296,13 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testOnDisplayChanged_hwcLayer() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
|
||||
decorationSupport.format = PixelFormat.R_8;
|
||||
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
|
||||
|
||||
// top cutout
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
|
||||
@@ -1390,18 +1319,16 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testOnDisplayChanged_nonHwcLayer() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
// top cutout
|
||||
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
|
||||
doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
|
||||
.when(mScreenDecorations).getCutout();
|
||||
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
|
||||
|
||||
mScreenDecorations.start();
|
||||
|
||||
final ScreenDecorations.DisplayCutoutView cutoutView =
|
||||
mScreenDecorations.mCutoutViews[BOUNDS_POSITION_TOP];
|
||||
final ScreenDecorations.DisplayCutoutView cutoutView = (ScreenDecorations.DisplayCutoutView)
|
||||
mScreenDecorations.getOverlayView(R.id.display_cutout);
|
||||
assertNotNull(cutoutView);
|
||||
spyOn(cutoutView);
|
||||
doReturn(mDisplay).when(cutoutView).getDisplay();
|
||||
|
||||
@@ -1414,8 +1341,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testHasSameProvidersWithNullOverlays() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, false /* fillCutout */, false /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
mScreenDecorations.start();
|
||||
|
||||
@@ -1433,8 +1359,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
public void testHasSameProvidersWithPrivacyDots() {
|
||||
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
|
||||
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
|
||||
0 /* roundedPadding */, true /* fillCutout */, true /* privacyDot */,
|
||||
false /* faceScanning */);
|
||||
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
|
||||
|
||||
mScreenDecorations.start();
|
||||
|
||||
@@ -1471,7 +1396,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
|
||||
private void setupResources(int radius, int radiusTop, int radiusBottom,
|
||||
@Nullable Drawable roundedTopDrawable, @Nullable Drawable roundedBottomDrawable,
|
||||
int roundedPadding, boolean fillCutout, boolean privacyDot, boolean faceScanning) {
|
||||
int roundedPadding, boolean privacyDot, boolean faceScanning) {
|
||||
mContext.getOrCreateTestableResources().addOverride(
|
||||
com.android.internal.R.array.config_displayUniqueIdArray,
|
||||
new String[]{});
|
||||
@@ -1511,8 +1436,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
}
|
||||
mContext.getOrCreateTestableResources().addOverride(
|
||||
R.dimen.rounded_corner_content_padding, roundedPadding);
|
||||
mContext.getOrCreateTestableResources().addOverride(
|
||||
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout);
|
||||
|
||||
mPrivacyDecorProviders = new ArrayList<>();
|
||||
if (privacyDot) {
|
||||
@@ -1531,19 +1454,4 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
when(mFaceScanningProviderFactory.getProviders()).thenReturn(mFaceScanningProviders);
|
||||
when(mFaceScanningProviderFactory.getHasProviders()).thenReturn(faceScanning);
|
||||
}
|
||||
|
||||
private DisplayCutout getDisplayCutoutForRotation(Insets safeInsets, Rect[] cutoutBounds) {
|
||||
final int rotation = mContext.getDisplay().getRotation();
|
||||
final Insets insets = RotationUtils.rotateInsets(safeInsets, rotation);
|
||||
final Rect[] sorted = new Rect[BOUNDS_POSITION_LENGTH];
|
||||
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
|
||||
final int rotatedPos = ScreenDecorations.getBoundPositionFromRotation(i, rotation);
|
||||
if (cutoutBounds[i] != null) {
|
||||
RotationUtils.rotateBounds(cutoutBounds[i], new Rect(0, 0, 100, 200), rotation);
|
||||
}
|
||||
sorted[rotatedPos] = cutoutBounds[i];
|
||||
}
|
||||
return new DisplayCutout(insets, sorted[BOUNDS_POSITION_LEFT], sorted[BOUNDS_POSITION_TOP],
|
||||
sorted[BOUNDS_POSITION_RIGHT], sorted[BOUNDS_POSITION_BOTTOM]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.decor
|
||||
|
||||
import android.graphics.Insets
|
||||
import android.graphics.Rect
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableResources
|
||||
import android.util.RotationUtils
|
||||
import android.util.Size
|
||||
import android.view.Display
|
||||
import android.view.DisplayCutout
|
||||
import android.view.DisplayCutout.BOUNDS_POSITION_LENGTH
|
||||
import android.view.DisplayInfo
|
||||
import android.view.Surface
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.util.mockito.any
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.doAnswer
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class CutoutDecorProviderFactoryTest : SysuiTestCase() {
|
||||
|
||||
@Mock private lateinit var display: Display
|
||||
private var testableRes: TestableResources? = null
|
||||
private lateinit var factory: CutoutDecorProviderFactory
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
testableRes = mContext.orCreateTestableResources
|
||||
factory = CutoutDecorProviderFactory(testableRes!!.resources, display)
|
||||
}
|
||||
|
||||
private fun setupFillCutout(fillCutout: Boolean) {
|
||||
testableRes!!.addOverride(
|
||||
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupDisplayInfo(
|
||||
displayCutout: DisplayCutout? = null,
|
||||
@Surface.Rotation rotation: Int = Surface.ROTATION_0,
|
||||
displayId: Int = -1
|
||||
) {
|
||||
doAnswer {
|
||||
it.getArgument<DisplayInfo>(0).let { info ->
|
||||
info.displayCutout = displayCutout
|
||||
info.rotation = rotation
|
||||
info.displayId = displayId
|
||||
}
|
||||
true
|
||||
}.`when`(display).getDisplayInfo(any<DisplayInfo>())
|
||||
}
|
||||
|
||||
private fun getCutout(
|
||||
safeInsets: Insets,
|
||||
cutoutBounds: Array<Rect?>,
|
||||
@Surface.Rotation rotation: Int = Surface.ROTATION_0,
|
||||
cutoutParentSizeForRotate: Size = Size(100, 200)
|
||||
): DisplayCutout {
|
||||
val insets = RotationUtils.rotateInsets(safeInsets, rotation)
|
||||
val sorted = arrayOfNulls<Rect>(BOUNDS_POSITION_LENGTH)
|
||||
for (pos in 0 until BOUNDS_POSITION_LENGTH) {
|
||||
val rotatedPos = (pos - rotation + BOUNDS_POSITION_LENGTH) % BOUNDS_POSITION_LENGTH
|
||||
if (cutoutBounds[pos] != null) {
|
||||
RotationUtils.rotateBounds(
|
||||
cutoutBounds[pos],
|
||||
cutoutParentSizeForRotate.width,
|
||||
cutoutParentSizeForRotate.height,
|
||||
rotation
|
||||
)
|
||||
}
|
||||
sorted[rotatedPos] = cutoutBounds[pos]
|
||||
}
|
||||
return DisplayCutout(
|
||||
insets,
|
||||
sorted[DisplayCutout.BOUNDS_POSITION_LEFT],
|
||||
sorted[DisplayCutout.BOUNDS_POSITION_TOP],
|
||||
sorted[DisplayCutout.BOUNDS_POSITION_RIGHT],
|
||||
sorted[DisplayCutout.BOUNDS_POSITION_BOTTOM]
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetNothingIfNoCutout() {
|
||||
setupFillCutout(false)
|
||||
|
||||
Assert.assertFalse(factory.hasProviders)
|
||||
Assert.assertEquals(0, factory.providers.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetTopCutoutProvider() {
|
||||
setupFillCutout(true)
|
||||
setupDisplayInfo(
|
||||
getCutout(
|
||||
safeInsets = Insets.of(0, 1, 0, 0),
|
||||
cutoutBounds = arrayOf(null, Rect(9, 0, 10, 1), null, null)
|
||||
)
|
||||
)
|
||||
|
||||
Assert.assertTrue(factory.hasProviders)
|
||||
|
||||
val providers = factory.providers
|
||||
Assert.assertEquals(1, providers.size)
|
||||
Assert.assertEquals(1, providers[0].numOfAlignedBound)
|
||||
Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_TOP, providers[0].alignedBounds[0])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetBottomCutoutProviderOnLandscape() {
|
||||
setupFillCutout(true)
|
||||
setupDisplayInfo(
|
||||
getCutout(
|
||||
safeInsets = Insets.of(0, 0, 0, 1),
|
||||
cutoutBounds = arrayOf(null, null, null, Rect(45, 199, 55, 200)),
|
||||
rotation = Surface.ROTATION_90
|
||||
),
|
||||
Surface.ROTATION_90
|
||||
)
|
||||
|
||||
Assert.assertTrue(factory.hasProviders)
|
||||
|
||||
val providers = factory.providers
|
||||
Assert.assertEquals(1, providers.size)
|
||||
Assert.assertEquals(1, providers[0].numOfAlignedBound)
|
||||
Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_BOTTOM, providers[0].alignedBounds[0])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetLeftCutoutProviderOnSeascape() {
|
||||
setupFillCutout(true)
|
||||
setupDisplayInfo(
|
||||
getCutout(
|
||||
safeInsets = Insets.of(1, 0, 0, 0),
|
||||
cutoutBounds = arrayOf(Rect(0, 20, 1, 40), null, null, null),
|
||||
rotation = Surface.ROTATION_270
|
||||
),
|
||||
Surface.ROTATION_270
|
||||
)
|
||||
|
||||
Assert.assertTrue(factory.hasProviders)
|
||||
|
||||
val providers = factory.providers
|
||||
Assert.assertEquals(1, providers.size)
|
||||
Assert.assertEquals(1, providers[0].numOfAlignedBound)
|
||||
Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_LEFT, providers[0].alignedBounds[0])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetTopRightCutoutProviderOnReverse() {
|
||||
setupFillCutout(true)
|
||||
setupDisplayInfo(
|
||||
getCutout(
|
||||
safeInsets = Insets.of(0, 1, 1, 0),
|
||||
cutoutBounds = arrayOf(
|
||||
null,
|
||||
Rect(9, 0, 10, 1),
|
||||
Rect(99, 40, 100, 60),
|
||||
null
|
||||
),
|
||||
rotation = Surface.ROTATION_180
|
||||
),
|
||||
Surface.ROTATION_180
|
||||
)
|
||||
|
||||
Assert.assertTrue(factory.hasProviders)
|
||||
|
||||
val providers = factory.providers
|
||||
Assert.assertEquals(2, providers.size)
|
||||
Assert.assertEquals(1, providers[0].numOfAlignedBound)
|
||||
Assert.assertEquals(1, providers[1].numOfAlignedBound)
|
||||
providers.sortedBy { it.alignedBounds[0] }.let {
|
||||
Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_TOP, it[0].alignedBounds[0])
|
||||
Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_RIGHT, it[1].alignedBounds[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user