blob: f81ef10e6b1408cbcdf3a6ca5f1ef841fc211de1 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.systemui;
import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
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;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
import static com.google.common.truth.Truth.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.IdRes;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.graphics.common.DisplayDecorationSupport;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.PathParser;
import android.util.Size;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowMetrics;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.decor.CornerDecorProvider;
import com.android.systemui.decor.CutoutDecorProviderFactory;
import com.android.systemui.decor.CutoutDecorProviderImpl;
import com.android.systemui.decor.DebugRoundedCornerModel;
import com.android.systemui.decor.DecorProvider;
import com.android.systemui.decor.DecorProviderFactory;
import com.android.systemui.decor.FaceScanningOverlayProviderImpl;
import com.android.systemui.decor.FaceScanningProviderFactory;
import com.android.systemui.decor.OverlayWindow;
import com.android.systemui.decor.PrivacyDotCornerDecorProviderImpl;
import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
import com.android.systemui.decor.RoundedCornerResDelegate;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.log.ScreenDecorationsLogger;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.List;
@RunWithLooper
@RunWith(AndroidTestingRunner.class)
@SmallTest
public class ScreenDecorationsTest extends SysuiTestCase {
private ScreenDecorations mScreenDecorations;
private WindowManager mWindowManager;
private DisplayManager mDisplayManager;
private SecureSettings mSecureSettings;
private FakeExecutor mExecutor;
private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
private FakeThreadFactory mThreadFactory;
private ArrayList<DecorProvider> mPrivacyDecorProviders;
private ArrayList<DecorProvider> mFaceScanningProviders;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
private AuthController mAuthController;
@Mock
private Display mDisplay;
@Mock
private CommandRegistry mCommandRegistry;
@Mock
private UserTracker mUserTracker;
@Mock
private PrivacyDotViewController mDotViewController;
@Mock
private TypedArray mMockTypedArray;
@Mock
private PrivacyDotDecorProviderFactory mPrivacyDotDecorProviderFactory;
@Mock
private FaceScanningProviderFactory mFaceScanningProviderFactory;
@Mock
private FaceScanningOverlayProviderImpl mFaceScanningDecorProvider;
@Mock
private CornerDecorProvider mPrivacyDotTopLeftDecorProvider;
@Mock
private CornerDecorProvider mPrivacyDotTopRightDecorProvider;
@Mock
private CornerDecorProvider mPrivacyDotBottomLeftDecorProvider;
@Mock
private CornerDecorProvider mPrivacyDotBottomRightDecorProvider;
@Mock
private Display.Mode mDisplayMode;
@Mock
private DisplayInfo mDisplayInfo;
private PrivacyDotViewController.ShowingListener mPrivacyDotShowingListener;
@Mock
private CutoutDecorProviderFactory mCutoutFactory;
@Captor
private ArgumentCaptor<AuthController.Callback> mAuthControllerCallback;
private List<DecorProvider> mMockCutoutList;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
Handler mainHandler = new Handler(TestableLooper.get(this).getLooper());
mSecureSettings = new FakeSettings();
mExecutor = new FakeExecutor(new FakeSystemClock());
mThreadFactory = new FakeThreadFactory(mExecutor);
mThreadFactory.setHandler(mainHandler);
mWindowManager = mock(WindowManager.class);
WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
.getMaximumWindowMetrics();
when(mWindowManager.getMaximumWindowMetrics()).thenReturn(metrics);
mContext.addMockSystemService(WindowManager.class, mWindowManager);
mDisplayManager = mock(DisplayManager.class);
mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
spyOn(mContext);
when(mContext.getDisplay()).thenReturn(mDisplay);
// Not support hwc layer by default
doReturn(null).when(mDisplay).getDisplayDecorationSupport();
doReturn(mDisplayMode).when(mDisplayInfo).getMode();
when(mMockTypedArray.length()).thenReturn(0);
mPrivacyDotTopLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
R.id.privacy_dot_top_left_container,
DisplayCutout.BOUNDS_POSITION_TOP,
DisplayCutout.BOUNDS_POSITION_LEFT,
R.layout.privacy_dot_top_left));
mPrivacyDotTopRightDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
R.id.privacy_dot_top_right_container,
DisplayCutout.BOUNDS_POSITION_TOP,
DisplayCutout.BOUNDS_POSITION_RIGHT,
R.layout.privacy_dot_top_right));
mPrivacyDotBottomLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
R.id.privacy_dot_bottom_left_container,
DisplayCutout.BOUNDS_POSITION_BOTTOM,
DisplayCutout.BOUNDS_POSITION_LEFT,
R.layout.privacy_dot_bottom_left));
mPrivacyDotBottomRightDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
R.id.privacy_dot_bottom_right_container,
DisplayCutout.BOUNDS_POSITION_BOTTOM,
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();
FakeFeatureFlags featureFlags = new FakeFeatureFlags();
featureFlags.set(Flags.STOP_PULSING_FACE_SCANNING_ANIMATION, true);
mFaceScanningDecorProvider = spy(new FaceScanningOverlayProviderImpl(
BOUNDS_POSITION_TOP,
mAuthController,
mStatusBarStateController,
mKeyguardUpdateMonitor,
mExecutor,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
featureFlags));
mScreenDecorations = spy(new ScreenDecorations(mContext, mSecureSettings,
mCommandRegistry, mUserTracker, mDisplayTracker, mDotViewController,
mThreadFactory,
mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
mAuthController) {
@Override
public void start() {
super.start();
mExecutor.runAllReady();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mExecutor.runAllReady();
}
@Override
protected void updateOverlayWindowVisibilityIfViewExists(@Nullable View view) {
super.updateOverlayWindowVisibilityIfViewExists(view);
mExecutor.runAllReady();
}
@Override
protected CutoutDecorProviderFactory getCutoutFactory() {
return ScreenDecorationsTest.this.mCutoutFactory;
}
});
mScreenDecorations.mDisplayInfo = mDisplayInfo;
// Make sure tests are never run starting in debug mode
mScreenDecorations.setDebug(false);
doReturn(1f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio();
doNothing().when(mScreenDecorations).updateOverlayProviderViews(any());
try {
mPrivacyDotShowingListener = mScreenDecorations.mPrivacyDotShowingListener.getClass()
.getDeclaredConstructor(ScreenDecorations.class)
.newInstance(mScreenDecorations);
} catch (Exception e) {
fail(e.getMessage());
}
}
@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 verifyRoundedCornerViewsExist(
@DisplayCutout.BoundsPosition final int overlayId,
@View.Visibility final boolean isExist) {
final View overlay = mScreenDecorations.mOverlays[overlayId].getRootView();
for (int id: getRoundCornerIdsFromOverlayId(overlayId)) {
final View view = overlay.findViewById(id);
if (isExist) {
assertNotNull(view);
assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
} else {
assertNull(view);
}
}
}
private void verifyFaceScanningViewExists(final boolean exists) {
final View overlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView();
final View view = overlay.findViewById(mFaceScanningDecorProvider.getViewId());
if (exists) {
assertNotNull(view);
} else {
assertNull(view);
}
}
@Nullable
private View findViewFromOverlays(@IdRes int id) {
for (OverlayWindow overlay: mScreenDecorations.mOverlays) {
if (overlay == null) {
continue;
}
View view = overlay.getRootView().findViewById(id);
if (view != null) {
return view;
}
}
return null;
}
private void verifyTopDotViewsNullable(final boolean isAssertNull) {
View tl = findViewFromOverlays(R.id.privacy_dot_top_left_container);
View tr = findViewFromOverlays(R.id.privacy_dot_top_right_container);
if (isAssertNull) {
assertNull(tl);
assertNull(tr);
} else {
assertNotNull(tl);
assertNotNull(tr);
}
}
private void verifyBottomDotViewsNullable(final boolean isAssertNull) {
View bl = findViewFromOverlays(R.id.privacy_dot_bottom_left_container);
View br = findViewFromOverlays(R.id.privacy_dot_bottom_right_container);
if (isAssertNull) {
assertNull(bl);
assertNull(br);
} else {
assertNotNull(bl);
assertNotNull(br);
}
}
private void verifyDotViewsNullable(final boolean isAssertNull) {
verifyTopDotViewsNullable(isAssertNull);
verifyBottomDotViewsNullable(isAssertNull);
}
private void verifyTopDotViewsVisibility(@View.Visibility final int visibility) {
verifyTopDotViewsNullable(false);
View tl = findViewFromOverlays(R.id.privacy_dot_top_left_container);
View tr = findViewFromOverlays(R.id.privacy_dot_top_right_container);
assertThat(tl.getVisibility()).isEqualTo(visibility);
assertThat(tr.getVisibility()).isEqualTo(visibility);
}
private void verifyBottomDotViewsVisibility(@View.Visibility final int visibility) {
verifyBottomDotViewsNullable(false);
View bl = findViewFromOverlays(R.id.privacy_dot_bottom_left_container);
View br = findViewFromOverlays(R.id.privacy_dot_bottom_right_container);
assertThat(bl.getVisibility()).isEqualTo(visibility);
assertThat(br.getVisibility()).isEqualTo(visibility);
}
private void verifyDotViewsVisibility(@View.Visibility final int visibility) {
verifyTopDotViewsVisibility(visibility);
verifyBottomDotViewsVisibility(visibility);
}
private void verifyOverlaysExistAndAdded(boolean left, boolean top, boolean right,
boolean bottom, @Nullable Integer visibilityIfExist) {
if (left || top || right || bottom) {
assertNotNull(mScreenDecorations.mOverlays);
} else {
assertNull(mScreenDecorations.mOverlays);
return;
}
if (left) {
final OverlayWindow overlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT];
assertNotNull(overlay);
verify(mWindowManager, times(1)).addView(
eq(overlay.getRootView()), any());
if (visibilityIfExist != null) {
assertEquals(visibilityIfExist.intValue(), overlay.getRootView().getVisibility());
}
} else {
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
}
if (top) {
final OverlayWindow overlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP];
assertNotNull(overlay);
verify(mWindowManager, times(1)).addView(
eq(overlay.getRootView()), any());
if (visibilityIfExist != null) {
assertEquals(visibilityIfExist.intValue(), overlay.getRootView().getVisibility());
}
} else {
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
}
if (right) {
final OverlayWindow overlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT];
assertNotNull(overlay);
verify(mWindowManager, times(1)).addView(
eq(overlay.getRootView()), any());
if (visibilityIfExist != null) {
assertEquals(visibilityIfExist.intValue(), overlay.getRootView().getVisibility());
}
} else {
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
}
if (bottom) {
final OverlayWindow overlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM];
assertNotNull(overlay);
verify(mWindowManager, times(1)).addView(
eq(overlay.getRootView()), any());
if (visibilityIfExist != null) {
assertEquals(visibilityIfExist.intValue(), overlay.getRootView().getVisibility());
}
} else {
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
}
}
@Test
public void testNoRounding_NoCutout_NoPrivacyDot_NoFaceScanning() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// no cutout (default)
mScreenDecorations.start();
// No views added.
verifyOverlaysExistAndAdded(false, false, false, false, null);
// No dot controller init
verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
public void testNoRounding_NoCutout_PrivacyDot_NoFaceScanning() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// no cutout (default)
mScreenDecorations.start();
// Top and bottom windows are created with INVISIBLE because of privacy dot only
// Left and right window should be null.
verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE);
verify(mDotViewController, times(1)).initialize(any(), any(), any(), any());
verify(mDotViewController, times(1)).setShowingListener(
mScreenDecorations.mPrivacyDotShowingListener);
// Rounded corner views shall not exist
verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false);
verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false);
// Privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
// Face scanning doesn't exist
verifyFaceScanningViewExists(false);
// Dot controller init
verify(mDotViewController, times(1)).initialize(
isA(View.class), isA(View.class), isA(View.class), isA(View.class));
}
@Test
public void testRounding_NoCutout_NoPrivacyDot_NoFaceScanning() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// no cutout (default)
mScreenDecorations.start();
// Top and bottom windows are created for rounded corners.
// Left and right window should be null.
verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
// Rounded corner views shall exist
verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
// Privacy dots shall not exist
verifyDotViewsNullable(true);
// Face scanning doesn't exist
verifyFaceScanningViewExists(false);
// No dot controller init
verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
public void testRounding_NoCutout_PrivacyDot_NoFaceScanning() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// no cutout (default)
mScreenDecorations.start();
// Top and bottom windows are created for rounded corners.
// Left and right window should be null.
verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
verify(mDotViewController, times(1)).initialize(any(), any(), any(), any());
verify(mDotViewController, times(1)).setShowingListener(null);
// Rounded corner views shall exist
verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
// Privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
// Face scanning doesn't exist
verifyFaceScanningViewExists(false);
// Dot controller init
verify(mDotViewController, times(1)).initialize(
isA(View.class), isA(View.class), isA(View.class), isA(View.class));
}
@Test
public void testRoundingRadius_NoCutout() {
final Size testRadiusPoint = new Size(3, 3);
setupResources(1 /* radius */, 1 /* radiusTop */, 1 /* radiusBottom */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded3px)
/* roundedTopDrawable */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded3px)
/* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// no cutout (default)
mScreenDecorations.start();
// Size of corner view should same as rounded_corner_radius{_top|_bottom}
final RoundedCornerResDelegate resDelegate = mScreenDecorations.mRoundedCornerResDelegate;
assertThat(resDelegate.getTopRoundedSize()).isEqualTo(testRadiusPoint);
assertThat(resDelegate.getBottomRoundedSize()).isEqualTo(testRadiusPoint);
}
@Test
public void testRoundingTopBottomRadius_OnTopBottomOverlay() {
setupResources(1 /* radius */, 1 /* radiusTop */, 1 /* radiusBottom */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded4px)
/* roundedTopDrawable */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded3px)
/* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// no cutout (default)
mScreenDecorations.start();
View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
.findViewById(R.id.rounded_corner_top_left);
View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
.findViewById(R.id.rounded_corner_top_right);
ViewGroup.LayoutParams leftParams = leftRoundedCorner.getLayoutParams();
ViewGroup.LayoutParams rightParams = rightRoundedCorner.getLayoutParams();
assertEquals(4, leftParams.width);
assertEquals(4, leftParams.height);
assertEquals(4, rightParams.width);
assertEquals(4, rightParams.height);
leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()
.findViewById(R.id.rounded_corner_bottom_left);
rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()
.findViewById(R.id.rounded_corner_bottom_right);
leftParams = leftRoundedCorner.getLayoutParams();
rightParams = rightRoundedCorner.getLayoutParams();
assertEquals(3, leftParams.width);
assertEquals(3, leftParams.height);
assertEquals(3, rightParams.width);
assertEquals(3, rightParams.height);
}
@Test
public void testRoundingTopBottomRadius_OnLeftRightOverlay() {
setupResources(1 /* radius */, 1 /* radiusTop */, 1 /* radiusBottom */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded3px)
/* roundedTopDrawable */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded5px)
/* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// left cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.start();
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(3, topParams.width);
assertEquals(3, topParams.height);
assertEquals(5, bottomParams.width);
assertEquals(5, bottomParams.height);
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(3, topParams.width);
assertEquals(3, topParams.height);
assertEquals(5, bottomParams.width);
assertEquals(5, bottomParams.height);
}
@Test
public void testNoRounding_CutoutShortEdge_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// top cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// Top window is created for top cutout.
// Bottom, left, or right window should be null.
verifyOverlaysExistAndAdded(false, true, false, false, View.VISIBLE);
// Privacy dots shall not exist because of no privacy
verifyDotViewsNullable(true);
// No dot controller init
verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
public void testNoRounding_CutoutShortEdge_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// top cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// Top window is created for top cutout.
// Bottom window is created for privacy dot.
// Left or right window should be null.
verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
verify(mDotViewController, times(1)).initialize(any(), any(), any(), any());
verify(mDotViewController, times(1)).setShowingListener(null);
// Top rounded corner views shall exist because of cutout
// but be gone because of no rounded corner
verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false);
// Bottom rounded corner views shall exist because of privacy dot
// but be gone because of no rounded corner
verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false);
// Privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
// Dot controller init
verify(mDotViewController, times(1)).initialize(
isA(View.class), isA(View.class), isA(View.class), isA(View.class));
}
@Test
public void testNoRounding_CutoutLongEdge_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// left cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.start();
// Left window is created for left cutout.
// Bottom, top, or right window should be null.
verifyOverlaysExistAndAdded(true, false, false, false, View.VISIBLE);
// Left rounded corner views shall exist because of cutout
// but be gone because of no rounded corner
verifyRoundedCornerViewsExist(BOUNDS_POSITION_LEFT, false);
// Top privacy dots shall not exist because of no privacy
verifyDotViewsNullable(true);
// No dot controller init
verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
public void testNoRounding_CutoutLongEdge_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// left cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.start();
// Left window is created for left cutout.
// Right window is created for privacy.
// Bottom, or top window should be null.
verifyOverlaysExistAndAdded(true, false, true, false, View.VISIBLE);
verify(mDotViewController, times(1)).initialize(any(), any(), any(), any());
verify(mDotViewController, times(1)).setShowingListener(null);
// Privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
// Dot controller init
verify(mDotViewController, times(1)).initialize(
isA(View.class), isA(View.class), isA(View.class), isA(View.class));
}
@Test
public void testRounding_CutoutShortEdge_NoPrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// top cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// Top window is created for rounded corner and top cutout.
// Bottom window is created for rounded corner.
// Left, or right window should be null.
verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
// Rounded corner views shall exist
verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
// Top privacy dots shall not exist because of no privacy dot
verifyDotViewsNullable(true);
// No dot controller init
verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
public void testRounding_CutoutShortEdge_PrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// top cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// Top window is created for rounded corner and top cutout.
// Bottom window is created for rounded corner.
// Left, or right window should be null.
verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
verify(mDotViewController, times(1)).initialize(any(), any(), any(), any());
verify(mDotViewController, times(1)).setShowingListener(null);
// Rounded corner views shall exist
verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
// Top privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
// Dot controller init
verify(mDotViewController, times(1)).initialize(
isA(View.class), isA(View.class), isA(View.class), isA(View.class));
}
@Test
public void testRounding_CutoutLongEdge_NoPrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// left cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.start();
// Left window is created for rounded corner and left cutout.
// Right window is created for rounded corner.
// Top, or bottom window should be null.
verifyOverlaysExistAndAdded(true, false, true, false, View.VISIBLE);
}
@Test
public void testRounding_CutoutLongEdge_PrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// left cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.start();
// Left window is created for rounded corner, left cutout, and privacy.
// Right window is created for rounded corner and privacy dot.
// Top, or bottom window should be null.
verifyOverlaysExistAndAdded(true, false, true, false, View.VISIBLE);
verify(mDotViewController, times(1)).initialize(any(), any(), any(), any());
verify(mDotViewController, times(1)).setShowingListener(null);
}
@Test
public void testRounding_CutoutShortAndLongEdge_NoPrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
20 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// top and left cutout
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.
// Bottom window is created for rounded corner.
// Left window is created for left cutout.
// Right window should be null.
verifyOverlaysExistAndAdded(true, true, false, true, View.VISIBLE);
}
@Test
public void testRounding_CutoutShortAndLongEdge_PrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
20 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// top and left cutout
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.
// Bottom window is created for rounded corner.
// Left window is created for left cutout.
// Right window should be null.
verifyOverlaysExistAndAdded(true, true, false, true, View.VISIBLE);
verify(mDotViewController, times(1)).initialize(any(), any(), any(), any());
verify(mDotViewController, times(1)).setShowingListener(null);
}
@Test
public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// Set to short edge cutout(top).
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
verifyOverlaysExistAndAdded(false, true, false, false, View.VISIBLE);
// Switch to long edge cutout(left).
mMockCutoutList.set(0, new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.onConfigurationChanged(new Configuration());
verifyOverlaysExistAndAdded(true, false, false, false, View.VISIBLE);
}
@Test
public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// Set to short edge cutout(top).
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
verify(mDotViewController, times(1)).initialize(any(), any(), any(), any());
verify(mDotViewController, times(1)).setShowingListener(null);
// Switch to long edge cutout(left).
mMockCutoutList.set(0, new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
mScreenDecorations.onConfigurationChanged(new Configuration());
verifyOverlaysExistAndAdded(true, false, true, false, View.VISIBLE);
verify(mDotViewController, times(2)).initialize(any(), any(), any(), any());
verify(mDotViewController, times(2)).setShowingListener(null);
// Verify each privacy dot id appears only once
mPrivacyDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> {
int findCount = 0;
for (OverlayWindow overlay: mScreenDecorations.mOverlays) {
if (overlay == null) {
continue;
}
final View view = overlay.getRootView().findViewById(viewId);
if (view != null) {
findCount++;
}
}
assertEquals(1, findCount);
});
}
@Test
public void testDelayedCutout_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// No cutout (default)
mScreenDecorations.start();
verifyOverlaysExistAndAdded(false, false, false, false, null);
// top cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.onConfigurationChanged(new Configuration());
// Only top windows should be added.
verifyOverlaysExistAndAdded(false, true, false, false, View.VISIBLE);
}
@Test
public void testDelayedCutout_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
// no cutout (default)
mScreenDecorations.start();
// Both top and bottom windows should be added with INVISIBLE because of only privacy dot,
// but rounded corners visibility shall be gone because of no rounding.
verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE);
verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false);
verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false);
verify(mDotViewController, times(1)).initialize(any(), any(), any(), any());
verify(mDotViewController, times(1)).setShowingListener(
mScreenDecorations.mPrivacyDotShowingListener);
// 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
// cutout, but rounded corners visibility shall be gone because of no rounding.
verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false);
verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false);
verify(mDotViewController, times(2)).initialize(any(), any(), any(), any());
verify(mDotViewController, times(1)).setShowingListener(null);
}
@Test
public void hasRoundedCornerOverlayFlagSet() {
assertThat(mScreenDecorations.getWindowLayoutParams(1).privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
is(PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY));
}
@Test
public void testUpdateRoundedCorners() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded3px)
/* roundedTopDrawable */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded4px)
/* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning*/);
mDisplayInfo.rotation = Surface.ROTATION_0;
mScreenDecorations.start();
final RoundedCornerResDelegate resDelegate = mScreenDecorations.mRoundedCornerResDelegate;
assertEquals(new Size(3, 3), resDelegate.getTopRoundedSize());
assertEquals(new Size(4, 4), resDelegate.getBottomRoundedSize());
doReturn(2f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio();
mDisplayInfo.rotation = Surface.ROTATION_270;
mScreenDecorations.onConfigurationChanged(null);
assertEquals(new Size(6, 6), resDelegate.getTopRoundedSize());
assertEquals(new Size(8, 8), resDelegate.getBottomRoundedSize());
}
@Test
public void testOnlyRoundedCornerRadiusTop() {
setupResources(0 /* radius */, 10 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
mScreenDecorations.start();
final RoundedCornerResDelegate resDelegate = mScreenDecorations.mRoundedCornerResDelegate;
assertEquals(true, resDelegate.getHasTop());
assertEquals(false, resDelegate.getHasBottom());
assertEquals(getDrawableIntrinsicSize(R.drawable.rounded_corner_top),
resDelegate.getTopRoundedSize());
final DecorProviderFactory mRoundedCornerFactory = mScreenDecorations.mRoundedCornerFactory;
assertEquals(true, mRoundedCornerFactory.getHasProviders());
final List<DecorProvider> providers = mRoundedCornerFactory.getProviders();
assertEquals(2, providers.size());
assertEquals(true, providers.get(0).getAlignedBounds().contains(BOUNDS_POSITION_TOP));
assertEquals(true, providers.get(1).getAlignedBounds().contains(BOUNDS_POSITION_TOP)); }
@Test
public void testOnlyRoundedCornerRadiusBottom() {
setupResources(0 /* radius */, 0 /* radiusTop */, 20 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
mScreenDecorations.start();
final RoundedCornerResDelegate resDelegate = mScreenDecorations.mRoundedCornerResDelegate;
assertEquals(false, resDelegate.getHasTop());
assertEquals(true, resDelegate.getHasBottom());
assertEquals(getDrawableIntrinsicSize(R.drawable.rounded_corner_bottom),
resDelegate.getBottomRoundedSize());
final DecorProviderFactory mRoundedCornerFactory = mScreenDecorations.mRoundedCornerFactory;
assertEquals(true, mRoundedCornerFactory.getHasProviders());
final List<DecorProvider> providers = mRoundedCornerFactory.getProviders();
assertEquals(2, providers.size());
assertEquals(true, providers.get(0).getAlignedBounds().contains(BOUNDS_POSITION_BOTTOM));
assertEquals(true, providers.get(1).getAlignedBounds().contains(BOUNDS_POSITION_BOTTOM));
}
@Test
public void testDebugRoundedCorners_noDeviceCornersSet() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
mScreenDecorations.start();
// No rounded corners exist at this point
verifyOverlaysExistAndAdded(false, false, false, false, View.VISIBLE);
// Path from rounded.xml, scaled by 10x to produce 80x80 corners
Path debugPath = PathParser.createPathFromPathData("M8,0H0v8C0,3.6,3.6,0,8,0z");
// WHEN debug corners are added to the delegate
DebugRoundedCornerModel debugCorner = new DebugRoundedCornerModel(
debugPath,
80,
80,
10f,
10f
);
mScreenDecorations.mDebugRoundedCornerDelegate
.applyNewDebugCorners(debugCorner, debugCorner);
// AND debug mode is entered
mScreenDecorations.setDebug(true);
mExecutor.runAllReady();
// THEN the debug corners provide decor
List<DecorProvider> providers = mScreenDecorations.getProviders(false);
assertEquals(4, providers.size());
// Top and bottom overlays contain the debug rounded corners
verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
}
@Test
public void testDebugRoundedCornersRemoved_noDeviceCornersSet() {
// GIVEN a device with no rounded corners defined
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
mScreenDecorations.start();
// No rounded corners exist at this point
verifyOverlaysExistAndAdded(false, false, false, false, View.VISIBLE);
// Path from rounded.xml, scaled by 10x to produce 80x80 corners
Path debugPath = PathParser.createPathFromPathData("M8,0H0v8C0,3.6,3.6,0,8,0z");
// WHEN debug corners are added to the delegate
DebugRoundedCornerModel debugCorner = new DebugRoundedCornerModel(
debugPath,
80,
80,
10f,
10f
);
mScreenDecorations.mDebugRoundedCornerDelegate
.applyNewDebugCorners(debugCorner, debugCorner);
// AND debug mode is entered
mScreenDecorations.setDebug(true);
mExecutor.runAllReady();
// Top and bottom overlays contain the debug rounded corners
verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
// WHEN debug is exited
mScreenDecorations.setDebug(false);
mExecutor.runAllReady();
// THEN the decor is removed
verifyOverlaysExistAndAdded(false, false, false, false, View.VISIBLE);
assertThat(mScreenDecorations.mDebugRoundedCornerDelegate.getHasBottom()).isFalse();
assertThat(mScreenDecorations.mDebugRoundedCornerDelegate.getHasTop()).isFalse();
}
@Test
public void testRegistration_From_NoOverlay_To_HasOverlays() {
doReturn(false).when(mScreenDecorations).hasOverlays();
mScreenDecorations.start();
assertThat(mScreenDecorations.mIsRegistered, is(false));
doReturn(true).when(mScreenDecorations).hasOverlays();
mScreenDecorations.onConfigurationChanged(new Configuration());
assertThat(mScreenDecorations.mIsRegistered, is(true));
}
@Test
public void testRegistration_From_HasOverlays_To_HasOverlays() {
doReturn(true).when(mScreenDecorations).hasOverlays();
mScreenDecorations.start();
assertThat(mScreenDecorations.mIsRegistered, is(true));
mScreenDecorations.onConfigurationChanged(new Configuration());
assertThat(mScreenDecorations.mIsRegistered, is(true));
}
@Test
public void testRegistration_From_HasOverlays_To_NoOverlay() {
doReturn(true).when(mScreenDecorations).hasOverlays();
mScreenDecorations.start();
assertThat(mScreenDecorations.mIsRegistered, is(true));
doReturn(false).when(mScreenDecorations).hasOverlays();
mScreenDecorations.onConfigurationChanged(new Configuration());
assertThat(mScreenDecorations.mIsRegistered, is(false));
}
@Test
public void testSupportHwcLayer_SwitchFrom_NotSupport() {
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
// top cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// should only inflate mOverlays when the hwc doesn't support screen decoration
assertNull(mScreenDecorations.mScreenDecorHwcWindow);
verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
decorationSupport.format = PixelFormat.R_8;
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
// Trigger the support hwc screen decoration change by changing the display unique id
mScreenDecorations.mDisplayUniqueId = "test";
mScreenDecorations.mDisplayListener.onDisplayChanged(1);
// should only inflate hwc layer when the hwc supports screen decoration
assertNotNull(mScreenDecorations.mScreenDecorHwcWindow);
verifyOverlaysExistAndAdded(false, false, false, false, null);
}
@Test
public void testNotSupportHwcLayer_SwitchFrom_Support() {
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
decorationSupport.format = PixelFormat.R_8;
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
// top cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// should only inflate hwc layer when the hwc supports screen decoration
assertNotNull(mScreenDecorations.mScreenDecorHwcWindow);
verifyOverlaysExistAndAdded(false, false, false, false, null);
doReturn(null).when(mDisplay).getDisplayDecorationSupport();
// Trigger the support hwc screen decoration change by changing the display unique id
mScreenDecorations.mDisplayUniqueId = "test";
mScreenDecorations.mDisplayListener.onDisplayChanged(1);
// should only inflate mOverlays when the hwc doesn't support screen decoration
assertNull(mScreenDecorations.mScreenDecorHwcWindow);
verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
}
@Test
public void faceSensorLocationChangesReloadsFaceScanningOverlay() {
mFaceScanningProviders = new ArrayList<>();
mFaceScanningProviders.add(mFaceScanningDecorProvider);
when(mFaceScanningProviderFactory.getProviders()).thenReturn(mFaceScanningProviders);
when(mFaceScanningProviderFactory.getHasProviders()).thenReturn(true);
ScreenDecorations screenDecorations = new ScreenDecorations(mContext,
mSecureSettings, mCommandRegistry, mUserTracker, mDisplayTracker,
mDotViewController,
mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")), mAuthController);
screenDecorations.start();
verify(mAuthController).addCallback(mAuthControllerCallback.capture());
when(mContext.getDisplay()).thenReturn(mDisplay);
when(mDisplay.getDisplayInfo(any())).thenAnswer(new Answer<Boolean>() {
@Override
public Boolean answer(InvocationOnMock invocation) throws Throwable {
DisplayInfo displayInfo = invocation.getArgument(0);
int modeId = 1;
displayInfo.modeId = modeId;
displayInfo.supportedModes = new Display.Mode[]{new Display.Mode(modeId, 1024, 1024,
90)};
return false;
}
});
mExecutor.runAllReady();
clearInvocations(mFaceScanningDecorProvider);
AuthController.Callback callback = mAuthControllerCallback.getValue();
callback.onFaceSensorLocationChanged();
mExecutor.runAllReady();
verify(mFaceScanningDecorProvider).onReloadResAndMeasure(any(),
anyInt(),
anyInt(),
anyInt(),
any());
}
@Test
public void testPrivacyDotShowingListenerWorkWellWithNullParameter() {
mPrivacyDotShowingListener.onPrivacyDotShown(null);
mPrivacyDotShowingListener.onPrivacyDotHidden(null);
}
@Test
public void testAutoShowHideOverlayWindowWhenSupportHwcLayer() {
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded3px)
/* roundedTopDrawable */,
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded4px)
/* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, true /* faceScanning */);
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
decorationSupport.format = PixelFormat.R_8;
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
// top cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// Inflate top and bottom overlay with INVISIBLE because of only privacy dots on sw layer
verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE);
// Make sure view found and window visibility changed as well
final View view = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()
.findViewById(R.id.privacy_dot_bottom_right_container);
view.setVisibility(View.VISIBLE);
mPrivacyDotShowingListener.onPrivacyDotShown(view);
assertEquals(View.VISIBLE,
mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView().getVisibility());
view.setVisibility(View.INVISIBLE);
mPrivacyDotShowingListener.onPrivacyDotHidden(view);
assertEquals(View.INVISIBLE,
mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView().getVisibility());
// Make sure face scanning view found and window visibility updates on camera protection
// update
final View faceScanView = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
.findViewById(mFaceScanningDecorProvider.getViewId());
when(mFaceScanningProviderFactory.shouldShowFaceScanningAnim()).thenReturn(true);
faceScanView.setVisibility(View.VISIBLE);
mScreenDecorations.showCameraProtection(new Path(), new Rect());
mExecutor.runAllReady();
assertEquals(View.VISIBLE,
mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView().getVisibility());
}
@Test
public void testAutoShowHideOverlayWindowWhenNoRoundedAndNoCutout() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, true /* faceScanning */);
// no cutout (default)
mScreenDecorations.start();
// Inflate top and bottom overlay with INVISIBLE because of only privacy dots on sw layer
verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE);
// Make sure view found and window visibility changed as well
final View view = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()
.findViewById(R.id.privacy_dot_bottom_right_container);
view.setVisibility(View.VISIBLE);
mPrivacyDotShowingListener.onPrivacyDotShown(view);
assertEquals(View.VISIBLE,
mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView().getVisibility());
view.setVisibility(View.INVISIBLE);
mPrivacyDotShowingListener.onPrivacyDotHidden(view);
assertEquals(View.INVISIBLE,
mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView().getVisibility());
// Make sure face scanning view found and window visibility updates on camera protection
// update
final View faceScanView = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
.findViewById(mFaceScanningDecorProvider.getViewId());
faceScanView.setVisibility(View.VISIBLE);
when(mFaceScanningProviderFactory.shouldShowFaceScanningAnim()).thenReturn(true);
mScreenDecorations.showCameraProtection(new Path(), new Rect());
mExecutor.runAllReady();
assertEquals(View.VISIBLE,
mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView().getVisibility());
}
@Test
public void testHwcLayer_noPrivacyDot_noFaceScanning() {
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
decorationSupport.format = PixelFormat.R_8;
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
// top cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
// Should only inflate hwc layer.
assertNotNull(mScreenDecorations.mScreenDecorHwcWindow);
verifyOverlaysExistAndAdded(false, false, false, false, null);
}
@Test
public void testHwcLayer_PrivacyDot_FaceScanning() {
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, true /* faceScanning */);
final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
decorationSupport.format = PixelFormat.R_8;
doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
// top cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
mScreenDecorations.start();
assertNotNull(mScreenDecorations.mScreenDecorHwcWindow);
// mOverlays are inflated but the visibility should be INVISIBLE.
verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE);
verify(mDotViewController, times(1)).initialize(any(), any(), any(), any());
verify(mDotViewController, times(1)).setShowingListener(
mScreenDecorations.mPrivacyDotShowingListener);
verifyFaceScanningViewExists(true);
}
@Test
public void testHasSameProvidersWithNullOverlays() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
mScreenDecorations.start();
final ArrayList<DecorProvider> newProviders = new ArrayList<>();
assertTrue(mScreenDecorations.hasSameProviders(newProviders));
newProviders.add(mPrivacyDotTopLeftDecorProvider);
assertFalse(mScreenDecorations.hasSameProviders(newProviders));
newProviders.add(mPrivacyDotTopRightDecorProvider);
assertFalse(mScreenDecorations.hasSameProviders(newProviders));
}
@Test
public void testHasSameProvidersWithPrivacyDots() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning */);
mScreenDecorations.start();
final ArrayList<DecorProvider> newProviders = new ArrayList<>();
assertFalse(mScreenDecorations.hasSameProviders(newProviders));
newProviders.add(mPrivacyDotTopLeftDecorProvider);
assertFalse(mScreenDecorations.hasSameProviders(newProviders));
newProviders.add(mPrivacyDotTopRightDecorProvider);
assertFalse(mScreenDecorations.hasSameProviders(newProviders));
newProviders.add(mPrivacyDotBottomLeftDecorProvider);
assertFalse(mScreenDecorations.hasSameProviders(newProviders));
newProviders.add(mPrivacyDotBottomRightDecorProvider);
assertTrue(mScreenDecorations.hasSameProviders(newProviders));
}
private Size getDrawableIntrinsicSize(@DrawableRes int drawableResId) {
final Drawable d = mContext.getDrawable(drawableResId);
return new Size(d.getIntrinsicWidth(), d.getIntrinsicHeight());
}
@Nullable
private Drawable getTestsDrawable(@DrawableRes int drawableId) {
try {
return mContext.createPackageContext("com.android.systemui.tests", 0)
.getDrawable(drawableId);
} catch (PackageManager.NameNotFoundException exception) {
return null;
}
}
private void setupResources(int radius, int radiusTop, int radiusBottom,
@Nullable Drawable roundedTopDrawable, @Nullable Drawable roundedBottomDrawable,
int roundedPadding, boolean privacyDot, boolean faceScanning) {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.array.config_displayUniqueIdArray,
new String[]{});
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.array.config_roundedCornerRadiusArray,
mMockTypedArray);
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.array.config_roundedCornerTopRadiusArray,
mMockTypedArray);
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.array.config_roundedCornerBottomRadiusArray,
mMockTypedArray);
mContext.getOrCreateTestableResources().addOverride(
R.array.config_roundedCornerDrawableArray,
mMockTypedArray);
mContext.getOrCreateTestableResources().addOverride(
R.array.config_roundedCornerTopDrawableArray,
mMockTypedArray);
mContext.getOrCreateTestableResources().addOverride(
R.array.config_roundedCornerBottomDrawableArray,
mMockTypedArray);
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.dimen.rounded_corner_radius, radius);
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.dimen.rounded_corner_radius_top, radiusTop);
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.dimen.rounded_corner_radius_bottom, radiusBottom);
if (roundedTopDrawable != null) {
mContext.getOrCreateTestableResources().addOverride(
R.drawable.rounded_corner_top,
roundedTopDrawable);
}
if (roundedBottomDrawable != null) {
mContext.getOrCreateTestableResources().addOverride(
R.drawable.rounded_corner_bottom,
roundedBottomDrawable);
}
mContext.getOrCreateTestableResources().addOverride(
R.dimen.rounded_corner_content_padding, roundedPadding);
mPrivacyDecorProviders = new ArrayList<>();
if (privacyDot) {
mPrivacyDecorProviders.add(mPrivacyDotTopLeftDecorProvider);
mPrivacyDecorProviders.add(mPrivacyDotTopRightDecorProvider);
mPrivacyDecorProviders.add(mPrivacyDotBottomLeftDecorProvider);
mPrivacyDecorProviders.add(mPrivacyDotBottomRightDecorProvider);
}
when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mPrivacyDecorProviders);
when(mPrivacyDotDecorProviderFactory.getHasProviders()).thenReturn(privacyDot);
mFaceScanningProviders = new ArrayList<>();
if (faceScanning) {
mFaceScanningProviders.add(mFaceScanningDecorProvider);
}
when(mFaceScanningProviderFactory.getProviders()).thenReturn(mFaceScanningProviders);
when(mFaceScanningProviderFactory.getHasProviders()).thenReturn(faceScanning);
}
}