diff options
Diffstat (limited to 'packages/SystemUI/tests')
55 files changed, 3609 insertions, 234 deletions
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index 62070422cc48..df670cf16477 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -94,6 +94,13 @@ <activity android:name="com.android.systemui.screenshot.RecyclerViewActivity" android:exported="false" /> + <!-- started from UsbDeviceSettingsManager --> + <activity android:name=".usb.UsbPermissionActivityTest$UsbPermissionActivityTestable" + android:exported="false" + android:theme="@style/Theme.SystemUI.Dialog.Alert" + android:finishOnCloseSystemDialogs="true" + android:excludeFromRecents="true" /> + <provider android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer" tools:replace="android:authorities" diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 06b0bb25e01c..8077deaf5fda 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -119,6 +119,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { when(mNotificationIcons.getLayoutParams()).thenReturn( mock(RelativeLayout.LayoutParams.class)); when(mView.getContext()).thenReturn(getContext()); + when(mView.getResources()).thenReturn(mResources); when(mView.findViewById(R.id.animatable_clock_view)).thenReturn(mClockView); when(mView.findViewById(R.id.animatable_clock_view_large)).thenReturn(mLargeClockView); @@ -127,7 +128,6 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { when(mLargeClockView.getContext()).thenReturn(getContext()); when(mView.isAttachedToWindow()).thenReturn(true); - when(mResources.getString(anyInt())).thenReturn("h:mm"); when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); mController = new KeyguardClockSwitchController( mView, @@ -142,7 +142,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { mBypassController, mSmartspaceController, mKeyguardUnlockAnimationController, - mSmartSpaceTransitionController + mSmartSpaceTransitionController, + mResources ); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 39d5314107ee..8dd5d6c01394 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -565,8 +565,9 @@ public class AuthControllerTest extends SysuiTestCase { credentialAllowed, true /* requireConfirmation */, 0 /* userId */, - "testPackage", 0 /* operationId */, + "testPackage", + 1 /* requestId */, BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT); } @@ -612,7 +613,7 @@ public class AuthControllerTest extends SysuiTestCase { @Override protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, boolean credentialAllowed, - String opPackageName, boolean skipIntro, long operationId, + String opPackageName, boolean skipIntro, long operationId, long requestId, @BiometricManager.BiometricMultiSensorMode int multiSensorConfig) { mLastBiometricPromptInfo = promptInfo; diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 1a390170c736..04ebee8913c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -16,6 +16,8 @@ package com.android.systemui.biometrics; +import static android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY; + import static junit.framework.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; @@ -55,7 +57,6 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; -import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -63,12 +64,14 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.Execution; import com.android.systemui.util.concurrency.FakeExecution; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.util.time.SystemClock; import org.junit.Before; import org.junit.Rule; @@ -120,8 +123,6 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock - private KeyguardViewMediator mKeyguardViewMediator; - @Mock private IUdfpsOverlayControllerCallback mUdfpsOverlayControllerCallback; @Mock private FalsingManager mFalsingManager; @@ -147,6 +148,10 @@ public class UdfpsControllerTest extends SysuiTestCase { private Handler mHandler; @Mock private ConfigurationController mConfigurationController; + @Mock + private SystemClock mSystemClock; + @Mock + private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; private FakeExecutor mFgExecutor; @@ -186,6 +191,7 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view, null)) .thenReturn(mKeyguardView); // for showOverlay REASON_AUTH_FPM_KEYGUARD when(mEnrollView.getContext()).thenReturn(mContext); + when(mKeyguardStateController.isOccluded()).thenReturn(false); final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>(); final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); @@ -216,7 +222,6 @@ public class UdfpsControllerTest extends SysuiTestCase { mStatusBarKeyguardViewManager, mDumpManager, mKeyguardUpdateMonitor, - mKeyguardViewMediator, mFalsingManager, mPowerManager, mAccessibilityManager, @@ -229,7 +234,9 @@ public class UdfpsControllerTest extends SysuiTestCase { mKeyguardBypassController, mDisplayManager, mHandler, - mConfigurationController); + mConfigurationController, + mSystemClock, + mUnlockedScreenOffAnimationController); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); @@ -413,6 +420,21 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test + public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException { + // GIVEN overlay was showing and the udfps bouncer is showing + mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); + when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true); + + // WHEN the overlay is hidden + mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID); + mFgExecutor.runAllReady(); + + // THEN the udfps bouncer is reset + verify(mStatusBarKeyguardViewManager).resetAlternateAuth(eq(true)); + } + + @Test public void testSubscribesToOrientationChangesWhenShowingOverlay() throws Exception { mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); @@ -564,5 +586,10 @@ public class UdfpsControllerTest extends SysuiTestCase { eq(mUdfpsController.EFFECT_CLICK), eq("udfps-onStart"), eq(UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES)); + + // THEN make sure vibration attributes has so that it always will play the haptic, + // even in battery saver mode + assertEquals(USAGE_ASSISTANCE_ACCESSIBILITY, + UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES.getUsage()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java index 0fbf9af159a6..4b35de15147b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -18,9 +18,9 @@ package com.android.systemui.biometrics; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeast; - import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -43,9 +43,11 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; @@ -88,7 +90,10 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { @Mock private ConfigurationController mConfigurationController; @Mock + private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; + @Mock private UdfpsController mUdfpsController; + private FakeSystemClock mSystemClock = new FakeSystemClock(); private UdfpsKeyguardViewController mController; @@ -119,12 +124,12 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { mStatusBar, mStatusBarKeyguardViewManager, mKeyguardUpdateMonitor, - mExecutor, mDumpManager, - mKeyguardViewMediator, mLockscreenShadeTransitionController, mConfigurationController, + mSystemClock, mKeyguardStateController, + mUnlockedScreenOffAnimationController, mUdfpsController); } @@ -299,6 +304,59 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { } @Test + public void testHiddenUdfpsBouncerOnTouchOutside_nothingHappens() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // GIVEN udfps bouncer isn't showing + mAltAuthInterceptor.hideAlternateAuthBouncer(); + + // WHEN touch is observed outside the view + mController.onTouchOutsideView(); + + // THEN bouncer / alt auth methods are never called + verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean()); + } + + @Test + public void testShowingUdfpsBouncerOnTouchOutsideWithinThreshold_nothingHappens() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // GIVEN udfps bouncer is showing + mAltAuthInterceptor.showAlternateAuthBouncer(); + + // WHEN touch is observed outside the view 200ms later (just within threshold) + mSystemClock.advanceTime(200); + mController.onTouchOutsideView(); + + // THEN bouncer / alt auth methods are never called because not enough time has passed + verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean()); + } + + @Test + public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showInputBouncer() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // GIVEN udfps bouncer is showing + mAltAuthInterceptor.showAlternateAuthBouncer(); + + // WHEN touch is observed outside the view 205ms later + mSystemClock.advanceTime(205); + mController.onTouchOutsideView(); + + // THEN show the bouncer and reset alt auth + verify(mStatusBarKeyguardViewManager).showBouncer(eq(true)); + verify(mStatusBarKeyguardViewManager).resetAlternateAuth(anyBoolean()); + } + + @Test public void testFadeInWithStatusBarExpansion() { // GIVEN view is attached mController.onViewAttached(); @@ -395,6 +453,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue(); } + + private void captureKeyguardStateControllerCallback() { verify(mKeyguardStateController).addCallback( mKeyguardStateControllerCallbackCaptor.capture()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index 81bae0bd6dd1..b8d465a8c881 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -18,6 +18,7 @@ package com.android.systemui.doze; import static com.android.systemui.doze.DozeMachine.State.DOZE; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_DOCKED; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING; import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE; @@ -29,6 +30,7 @@ import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -42,11 +44,11 @@ import android.os.PowerManager; import android.os.UserHandle; import android.provider.Settings; import android.testing.AndroidTestingRunner; -import android.view.Display; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; @@ -84,6 +86,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { @Mock DozeParameters mDozeParameters; @Mock + DockManager mDockManager; + @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); private FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor); @@ -112,9 +116,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mSensor = fakeSensorManager.getFakeLightSensor(); mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, Optional.of(mSensor.getSensor()), mDozeHost, null /* handler */, - mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, + mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager, mUnlockedScreenOffAnimationController); - mScreen.onScreenState(Display.STATE_ON); } @Test @@ -126,18 +129,9 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test - public void testAod_usesLightSensor() { - mScreen.onScreenState(Display.STATE_DOZE); - waitForSensorManager(); - - mSensor.sendSensorEvent(3); - - assertEquals(3, mServiceFake.screenBrightness); - } - - @Test public void testAod_usesDebugValue() throws Exception { - mScreen.onScreenState(Display.STATE_DOZE); + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); waitForSensorManager(); Intent intent = new Intent(DozeScreenBrightness.ACTION_AOD_BRIGHTNESS); @@ -160,10 +154,53 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test + public void doze_doesNotUseLightSensor() { + // GIVEN the device is docked and the display state changes to ON + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE); + waitForSensorManager(); + + // WHEN new sensor event sent + mSensor.sendSensorEvent(3); + + // THEN brightness is NOT changed, it's set to the default brightness + assertNotSame(3, mServiceFake.screenBrightness); + assertEquals(DEFAULT_BRIGHTNESS, mServiceFake.screenBrightness); + } + + @Test + public void aod_usesLightSensor() { + // GIVEN the device is docked and the display state changes to ON + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + waitForSensorManager(); + + // WHEN new sensor event sent + mSensor.sendSensorEvent(3); + + // THEN brightness is updated + assertEquals(3, mServiceFake.screenBrightness); + } + + @Test + public void docked_usesLightSensor() { + // GIVEN the device is docked and the display state changes to ON + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + mScreen.transitionTo(DOZE_AOD, DOZE_AOD_DOCKED); + waitForSensorManager(); + + // WHEN new sensor event sent + mSensor.sendSensorEvent(3); + + // THEN brightness is updated + assertEquals(3, mServiceFake.screenBrightness); + } + + @Test public void testPausingAod_doesNotResetBrightness() throws Exception { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); - mScreen.onScreenState(Display.STATE_DOZE); waitForSensorManager(); mSensor.sendSensorEvent(1); @@ -178,7 +215,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() throws Exception { mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, Optional.empty() /* sensor */, mDozeHost, null /* handler */, - mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, + mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager, mUnlockedScreenOffAnimationController); mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE); @@ -205,37 +242,22 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test - public void testOnScreenStateSetBeforeTransition_stillRegistersSensor() { - mScreen.transitionTo(UNINITIALIZED, INITIALIZED); - mScreen.onScreenState(Display.STATE_DOZE); - mScreen.transitionTo(INITIALIZED, DOZE_AOD); - waitForSensorManager(); - - mSensor.sendSensorEvent(1); - - assertEquals(1, mServiceFake.screenBrightness); - } - - @Test public void testNullSensor() throws Exception { mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, Optional.empty() /* sensor */, mDozeHost, null /* handler */, - mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, + mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager, mUnlockedScreenOffAnimationController); mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING); mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED); - mScreen.onScreenState(Display.STATE_DOZE); - mScreen.onScreenState(Display.STATE_OFF); } @Test public void testNoBrightnessDeliveredAfterFinish() throws Exception { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); - mScreen.onScreenState(Display.STATE_DOZE); mScreen.transitionTo(DOZE_AOD, FINISH); waitForSensorManager(); @@ -248,7 +270,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim() { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); - mScreen.onScreenState(Display.STATE_DOZE); waitForSensorManager(); mSensor.sendSensorEvent(1); @@ -262,7 +283,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { public void pausingAod_unblanksAfterSensor() { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); - mScreen.onScreenState(Display.STATE_DOZE); waitForSensorManager(); mSensor.sendSensorEvent(2); @@ -274,7 +294,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { reset(mDozeHost); mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD); - mScreen.onScreenState(Display.STATE_DOZE); waitForSensorManager(); mSensor.sendSensorEvent(2); verify(mDozeHost).setAodDimmingScrim(eq(0f)); @@ -284,7 +303,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { public void pausingAod_unblanksIfSensorWasAlwaysReady() throws Exception { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); - mScreen.onScreenState(Display.STATE_DOZE); waitForSensorManager(); mSensor.sendSensorEvent(2); @@ -301,13 +319,14 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn( PowerManager.GO_TO_SLEEP_REASON_TIMEOUT); when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true); + when(mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()).thenReturn(true); mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE); // If we're dozing after a timeout, and playing the unlocked screen animation, we should - // stay at dim brightness, because the screen dims just before timeout. - assertEquals(mServiceFake.screenBrightness, DIM_BRIGHTNESS); + // stay at or below dim brightness, because the screen dims just before timeout. + assertTrue(mServiceFake.screenBrightness <= DIM_BRIGHTNESS); } @Test @@ -315,6 +334,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn( PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON); when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true); + when(mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()).thenReturn(true); mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE); @@ -329,6 +349,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn( PowerManager.GO_TO_SLEEP_REASON_TIMEOUT); when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(false); + when(mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()).thenReturn(false); mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java index 0c94f09c8572..5c4c27ccc4ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -88,7 +88,7 @@ public class DozeSensorsTest extends SysuiTestCase { private FakeSettings mFakeSettings = new FakeSettings(); private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener; private TestableLooper mTestableLooper; - private DozeSensors mDozeSensors; + private TestableDozeSensors mDozeSensors; private TriggerSensor mSensorTap; @Before @@ -170,6 +170,94 @@ public class DozeSensorsTest extends SysuiTestCase { assertTrue(mSensorTap.mRequested); } + @Test + public void testDozeSensorSetListening() { + // GIVEN doze sensors enabled + when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true); + + // GIVEN a trigger sensor + Sensor mockSensor = mock(Sensor.class); + TriggerSensor triggerSensor = mDozeSensors.createDozeSensor( + mockSensor, + /* settingEnabled */ true, + /* requiresTouchScreen */ true); + when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor))) + .thenReturn(true); + + // WHEN we want to listen for the trigger sensor + triggerSensor.setListening(true); + + // THEN the sensor is registered + assertTrue(triggerSensor.mRegistered); + } + + @Test + public void testDozeSensorSettingDisabled() { + // GIVEN doze sensors enabled + when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true); + + // GIVEN a trigger sensor + Sensor mockSensor = mock(Sensor.class); + TriggerSensor triggerSensor = mDozeSensors.createDozeSensor( + mockSensor, + /* settingEnabled*/ false, + /* requiresTouchScreen */ true); + when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor))) + .thenReturn(true); + + // WHEN setListening is called + triggerSensor.setListening(true); + + // THEN the sensor is not registered + assertFalse(triggerSensor.mRegistered); + } + + @Test + public void testDozeSensorIgnoreSetting() { + // GIVEN doze sensors enabled + when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true); + + // GIVEN a trigger sensor that's + Sensor mockSensor = mock(Sensor.class); + TriggerSensor triggerSensor = mDozeSensors.createDozeSensor( + mockSensor, + /* settingEnabled*/ false, + /* requiresTouchScreen */ true); + when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor))) + .thenReturn(true); + + // GIVEN sensor is listening + triggerSensor.setListening(true); + + // WHEN ignoreSetting is called + triggerSensor.ignoreSetting(true); + + // THEN the sensor is registered + assertTrue(triggerSensor.mRegistered); + } + + @Test + public void testUpdateListeningAfterAlreadyRegistered() { + // GIVEN doze sensors enabled + when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true); + + // GIVEN a trigger sensor + Sensor mockSensor = mock(Sensor.class); + TriggerSensor triggerSensor = mDozeSensors.createDozeSensor( + mockSensor, + /* settingEnabled*/ true, + /* requiresTouchScreen */ true); + when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor))) + .thenReturn(true); + + // WHEN setListening is called AND updateListening is called + triggerSensor.setListening(true); + triggerSensor.updateListening(); + + // THEN the sensor is still registered + assertTrue(triggerSensor.mRegistered); + } + private class TestableDozeSensors extends DozeSensors { TestableDozeSensors() { @@ -187,5 +275,17 @@ public class DozeSensorsTest extends SysuiTestCase { } mSensors = new TriggerSensor[] {mTriggerSensor, mSensorTap}; } + + public TriggerSensor createDozeSensor(Sensor sensor, boolean settingEnabled, + boolean requiresTouchScreen) { + return new TriggerSensor(/* sensor */ sensor, + /* setting name */ "test_setting", + /* settingDefault */ settingEnabled, + /* configured */ true, + /* pulseReason*/ 0, + /* reportsTouchCoordinate*/ false, + requiresTouchScreen, + mDozeLog); + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 10997fab081f..9577c7a2d6fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -45,6 +45,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.concurrency.FakeThreadFactory; import com.android.systemui.util.sensors.AsyncSensorManager; @@ -83,6 +84,8 @@ public class DozeTriggersTest extends SysuiTestCase { private AuthController mAuthController; @Mock private UiEventLogger mUiEventLogger; + @Mock + private KeyguardStateController mKeyguardStateController; private DozeTriggers mTriggers; private FakeSensorManager mSensors; @@ -114,7 +117,7 @@ public class DozeTriggersTest extends SysuiTestCase { mTriggers = new DozeTriggers(mContext, mHost, config, dozeParameters, asyncSensorManager, wakeLock, mDockManager, mProximitySensor, mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(), - mAuthController, mExecutor, mUiEventLogger); + mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController); mTriggers.setDozeMachine(mMachine); waitForSensorManager(); } @@ -217,6 +220,32 @@ public class DozeTriggersTest extends SysuiTestCase { } @Test + public void testPickupGesture() { + // GIVEN device is in doze (screen blank, but running doze sensors) + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + + // WHEN the pick up gesture is triggered and keyguard isn't occluded + when(mKeyguardStateController.isOccluded()).thenReturn(false); + mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null); + + // THEN wakeup + verify(mMachine).wakeUp(); + } + + @Test + public void testPickupGestureDroppedKeyguardOccluded() { + // GIVEN device is in doze (screen blank, but running doze sensors) + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + + // WHEN the pick up gesture is triggered and keyguard IS occluded + when(mKeyguardStateController.isOccluded()).thenReturn(true); + mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null); + + // THEN never wakeup + verify(mMachine, never()).wakeUp(); + } + + @Test public void testOnSensor_Fingerprint() { // GIVEN dozing state when(mMachine.getState()).thenReturn(DOZE_AOD); diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java index 578c2d976cce..509ef82ee426 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -51,6 +52,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; @@ -112,6 +114,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { @Mock private Handler mHandler; @Mock private UserContextProvider mUserContextProvider; @Mock private StatusBar mStatusBar; + @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; private TestableLooper mTestableLooper; @@ -156,7 +159,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { mSysUiState, mHandler, mPackageManager, - mStatusBar + mStatusBar, + mKeyguardUpdateMonitor ); mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting(); @@ -422,4 +426,31 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { restartAction.onLongPress(); verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_REBOOT_LONG_PRESS); } + + @Test + public void testOnLockScreen_disableSmartLock() { + mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite); + int user = KeyguardUpdateMonitor.getCurrentUser(); + doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems(); + doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any()); + doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any()); + doReturn(false).when(mStatusBar).isKeyguardShowing(); + String[] actions = { + GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY, + GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN, + GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER, + GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART, + }; + doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions(); + + // When entering power menu from lockscreen, with smart lock enabled + when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true); + mGlobalActionsDialogLite.showOrHideDialog(true, true); + + // Then smart lock will be disabled + verify(mLockPatternUtils).requireCredentialEntry(eq(user)); + + // hide dialog again + mGlobalActionsDialogLite.showOrHideDialog(true, true); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index 2fa67cc0be60..338bb3037331 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -56,6 +56,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; @@ -126,6 +127,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase { @Mock private PackageManager mPackageManager; @Mock private SecureSettings mSecureSettings; @Mock private StatusBar mStatusBar; + @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; private TestableLooper mTestableLooper; @@ -169,7 +171,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase { mSysUiState, mHandler, mPackageManager, - mStatusBar + mStatusBar, + mKeyguardUpdateMonitor ); mGlobalActionsDialog.setZeroDialogPressDelayForTesting(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java new file mode 100644 index 000000000000..df112840ed87 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2021 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.keyguard; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyObject; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.res.Resources; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.keyguard.AnimatableClockController; +import com.android.keyguard.AnimatableClockView; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.settingslib.Utils; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.policy.BatteryController; + +import org.junit.After; +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.MockitoSession; +import org.mockito.quality.Strictness; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class AnimatableClockControllerTest extends SysuiTestCase { + @Mock + private AnimatableClockView mClockView; + @Mock + private StatusBarStateController mStatusBarStateController; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private BatteryController mBatteryController; + @Mock + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + private KeyguardBypassController mBypassController; + @Mock + private Resources mResources; + + private MockitoSession mStaticMockSession; + private AnimatableClockController mAnimatableClockController; + + // Capture listeners so that they can be used to send events + @Captor private ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); + private View.OnAttachStateChangeListener mAttachListener; + + @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateCaptor; + private StatusBarStateController.StateListener mStatusBarStateCallback; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mStaticMockSession = mockitoSession() + .mockStatic(Utils.class) + .strictness(Strictness.LENIENT) // it's ok if mocked classes aren't used + .startMocking(); + when(Utils.getColorAttrDefaultColor(anyObject(), anyInt())).thenReturn(0); + + mAnimatableClockController = new AnimatableClockController( + mClockView, + mStatusBarStateController, + mBroadcastDispatcher, + mBatteryController, + mKeyguardUpdateMonitor, + mBypassController, + mResources + ); + mAnimatableClockController.init(); + captureAttachListener(); + } + + @After + public void tearDown() { + mStaticMockSession.finishMocking(); + } + + @Test + public void testOnAttachedUpdatesDozeStateToTrue() { + // GIVEN dozing + when(mStatusBarStateController.isDozing()).thenReturn(true); + when(mStatusBarStateController.getDozeAmount()).thenReturn(1f); + + // WHEN the clock view gets attached + mAttachListener.onViewAttachedToWindow(mClockView); + + // THEN the clock controller updated its dozing state to true + assertTrue(mAnimatableClockController.isDozing()); + } + + @Test + public void testOnAttachedUpdatesDozeStateToFalse() { + // GIVEN not dozing + when(mStatusBarStateController.isDozing()).thenReturn(false); + when(mStatusBarStateController.getDozeAmount()).thenReturn(0f); + + // WHEN the clock view gets attached + mAttachListener.onViewAttachedToWindow(mClockView); + + // THEN the clock controller updated its dozing state to false + assertFalse(mAnimatableClockController.isDozing()); + } + + private void captureAttachListener() { + verify(mClockView).addOnAttachStateChangeListener(mAttachCaptor.capture()); + mAttachListener = mAttachCaptor.getValue(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 1dacc6245d84..31d70f5c811f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -33,8 +34,9 @@ import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; import android.os.PowerManager; import android.os.PowerManager.WakeLock; +import android.telephony.TelephonyManager; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; +import android.testing.TestableLooper; import androidx.test.filters.SmallTest; @@ -65,7 +67,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) -@RunWithLooper +@TestableLooper.RunWithLooper @SmallTest public class KeyguardViewMediatorTest extends SysuiTestCase { private KeyguardViewMediator mViewMediator; @@ -160,4 +162,29 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mViewMediator.onDozeAmountChanged(1f, 1f); assertFalse(mViewMediator.isAnimatingScreenOff()); } + + @Test + @TestableLooper.RunWithLooper(setAsMainLooper = true) + public void restoreBouncerWhenSimLockedAndKeyguardIsGoingAway() { + // When showing and provisioned + mViewMediator.onSystemReady(); + when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true); + mViewMediator.setShowingLocked(true); + + // and a SIM becomes locked and requires a PIN + mViewMediator.mUpdateCallback.onSimStateChanged( + 1 /* subId */, + 0 /* slotId */, + TelephonyManager.SIM_STATE_PIN_REQUIRED); + + // and the keyguard goes away + mViewMediator.setShowingLocked(false); + when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false); + mViewMediator.mUpdateCallback.onKeyguardVisibilityChanged(false); + + TestableLooper.get(this).processAllMessages(); + + // then make sure it comes back + verify(mStatusBarKeyguardViewManager, atLeast(1)).show(null); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java index 9c3016c57ccf..9356c547db70 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java @@ -31,6 +31,7 @@ import android.os.Vibrator; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.DisplayMetrics; +import android.util.Pair; import android.view.View; import android.view.accessibility.AccessibilityManager; @@ -119,26 +120,15 @@ public class LockIconViewControllerTest extends SysuiTestCase { mAccessibilityManager, mConfigurationController, mDelayableExecutor, - mVibrator + mVibrator, + mAuthRippleController ); } @Test public void testUpdateFingerprintLocationOnInit() { // GIVEN fp sensor location is available pre-attached - final PointF udfpsLocation = new PointF(50, 75); - final int radius = 33; - final FingerprintSensorPropertiesInternal fpProps = - new FingerprintSensorPropertiesInternal( - /* sensorId */ 0, - /* strength */ 0, - /* max enrollments per user */ 5, - /* component info */ new ArrayList<>(), - /* sensorType */ 3, - /* resetLockoutRequiresHwToken */ false, - (int) udfpsLocation.x, (int) udfpsLocation.y, radius); - when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation); - when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps)); + Pair<Integer, PointF> udfps = setupUdfps(); // WHEN lock icon view controller is initialized and attached mLockIconViewController.init(); @@ -146,8 +136,8 @@ public class LockIconViewControllerTest extends SysuiTestCase { mAttachListener.onViewAttachedToWindow(mLockIconView); // THEN lock icon view location is updated with the same coordinates as fpProps - verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius)); - assertEquals(udfpsLocation, mPointCaptor.getValue()); + verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(udfps.first)); + assertEquals(udfps.second, mPointCaptor.getValue()); } @Test @@ -161,6 +151,47 @@ public class LockIconViewControllerTest extends SysuiTestCase { // GIVEN fp sensor location is available post-atttached captureAuthControllerCallback(); + Pair<Integer, PointF> udfps = setupUdfps(); + + // WHEN all authenticators are registered + mAuthControllerCallback.onAllAuthenticatorsRegistered(); + + // THEN lock icon view location is updated with the same coordinates as fpProps + verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(udfps.first)); + assertEquals(udfps.second, mPointCaptor.getValue()); + } + + @Test + public void testLockIconViewBackgroundEnabledWhenUdfpsIsAvailable() { + // GIVEN Udpfs sensor location is available + setupUdfps(); + + mLockIconViewController.init(); + captureAttachListener(); + + // WHEN the view is attached + mAttachListener.onViewAttachedToWindow(mLockIconView); + + // THEN the lock icon view background should be enabled + verify(mLockIconView).setUseBackground(true); + } + + @Test + public void testLockIconViewBackgroundDisabledWhenUdfpsIsUnavailable() { + // GIVEN Udfps sensor location is not available + when(mAuthController.getUdfpsSensorLocation()).thenReturn(null); + + mLockIconViewController.init(); + captureAttachListener(); + + // WHEN the view is attached + mAttachListener.onViewAttachedToWindow(mLockIconView); + + // THEN the lock icon view background should be disabled + verify(mLockIconView).setUseBackground(false); + } + + private Pair<Integer, PointF> setupUdfps() { final PointF udfpsLocation = new PointF(50, 75); final int radius = 33; final FingerprintSensorPropertiesInternal fpProps = @@ -175,12 +206,7 @@ public class LockIconViewControllerTest extends SysuiTestCase { when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation); when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps)); - // WHEN all authenticators are registered - mAuthControllerCallback.onAllAuthenticatorsRegistered(); - - // THEN lock icon view location is updated with the same coordinates as fpProps - verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius)); - assertEquals(udfpsLocation, mPointCaptor.getValue()); + return new Pair(radius, udfpsLocation); } private void captureAuthControllerCallback() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index b129fdd0d844..42629f545559 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -37,6 +37,7 @@ import android.widget.TextView import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.LiveData import androidx.test.filters.SmallTest +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.media.dialog.MediaOutputDialogFactory import com.android.systemui.plugins.ActivityStarter @@ -101,7 +102,6 @@ public class MediaControlPanelTest : SysuiTestCase() { private lateinit var seamless: ViewGroup private lateinit var seamlessIcon: ImageView private lateinit var seamlessText: TextView - private lateinit var seamlessFallback: ImageView private lateinit var seekBar: SeekBar private lateinit var elapsedTimeView: TextView private lateinit var totalTimeView: TextView @@ -154,8 +154,6 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(holder.seamlessIcon).thenReturn(seamlessIcon) seamlessText = TextView(context) whenever(holder.seamlessText).thenReturn(seamlessText) - seamlessFallback = ImageView(context) - whenever(holder.seamlessFallback).thenReturn(seamlessFallback) seekBar = SeekBar(context) whenever(holder.seekBar).thenReturn(seekBar) elapsedTimeView = TextView(context) @@ -239,21 +237,19 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindDisabledDevice() { seamless.id = 1 - seamlessFallback.id = 2 + val fallbackString = context.getString(R.string.media_seamless_other_device) player.attachPlayer(holder) val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice, true, null) player.bindPlayer(state, PACKAGE) - verify(expandedSet).setVisibility(seamless.id, View.GONE) - verify(expandedSet).setVisibility(seamlessFallback.id, View.VISIBLE) - verify(collapsedSet).setVisibility(seamless.id, View.GONE) - verify(collapsedSet).setVisibility(seamlessFallback.id, View.VISIBLE) + assertThat(seamless.isEnabled()).isFalse() + assertThat(seamlessText.getText()).isEqualTo(fallbackString) + assertThat(seamless.contentDescription).isEqualTo(fallbackString) } @Test fun bindNullDevice() { - val fallbackString = context.getResources().getString( - com.android.internal.R.string.ext_media_seamless_action) + val fallbackString = context.getResources().getString(R.string.media_seamless_other_device) player.attachPlayer(holder) val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt index d8791867cb45..28aed2085528 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt @@ -86,7 +86,7 @@ class MediaDataFilterTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) - mediaDataFilter = MediaDataFilter(broadcastDispatcher, mediaResumeListener, + mediaDataFilter = MediaDataFilter(context, broadcastDispatcher, mediaResumeListener, lockscreenUserManager, executor, clock) mediaDataFilter.mediaDataManager = mediaDataManager mediaDataFilter.addListener(listener) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index ba6dfd3c453a..47c5545ab587 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -4,6 +4,7 @@ import android.app.Notification.MediaStyle import android.app.PendingIntent import android.app.smartspace.SmartspaceAction import android.app.smartspace.SmartspaceTarget +import android.content.Intent import android.graphics.Bitmap import android.media.MediaDescription import android.media.MediaMetadata @@ -51,6 +52,7 @@ private const val APP_NAME = "SystemUI" private const val SESSION_ARTIST = "artist" private const val SESSION_TITLE = "title" private const val USER_ID = 0 +private val DISMISS_INTENT = Intent().apply { action = "dismiss" } private fun <T> anyObject(): T { return Mockito.anyObject<T>() @@ -83,6 +85,7 @@ class MediaDataManagerTest : SysuiTestCase() { lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider @Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget @Mock private lateinit var mediaRecommendationItem: SmartspaceAction + @Mock private lateinit var mediaSmartspaceBaseAction: SmartspaceAction lateinit var mediaDataManager: MediaDataManager lateinit var mediaNotification: StatusBarNotification @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData> @@ -146,8 +149,12 @@ class MediaDataManagerTest : SysuiTestCase() { // treat mediaSessionBasedFilter as a listener for testing. listener = mediaSessionBasedFilter - val recommendationExtras = Bundle() - recommendationExtras.putString("package_name", PACKAGE_NAME) + val recommendationExtras = Bundle().apply { + putString("package_name", PACKAGE_NAME) + putParcelable("dismiss_intent", DISMISS_INTENT) + } + whenever(mediaSmartspaceBaseAction.extras).thenReturn(recommendationExtras) + whenever(mediaSmartspaceTarget.baseAction).thenReturn(mediaSmartspaceBaseAction) whenever(mediaRecommendationItem.extras).thenReturn(recommendationExtras) whenever(mediaSmartspaceTarget.smartspaceTargetId).thenReturn(KEY_MEDIA_SMARTSPACE) whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA) @@ -163,7 +170,7 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test - fun testSetTimedOut_deactivatesMedia() { + fun testSetTimedOut_active_deactivatesMedia() { val data = MediaData(userId = USER_ID, initialized = true, backgroundColor = 0, app = null, appIcon = null, artist = null, song = null, artwork = null, actions = emptyList(), actionsToShowInCompact = emptyList(), packageName = "INVALID", token = null, @@ -176,6 +183,25 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test + fun testSetTimedOut_resume_dismissesMedia() { + // WHEN resume controls are present, and time out + val desc = MediaDescription.Builder().run { + setTitle(SESSION_TITLE) + build() + } + mediaDataManager.addResumptionControls(USER_ID, desc, Runnable {}, session.sessionToken, + APP_NAME, pendingIntent, PACKAGE_NAME) + backgroundExecutor.runAllReady() + foregroundExecutor.runAllReady() + mediaDataManager.setTimedOut(PACKAGE_NAME, timedOut = true) + + // THEN it is removed and listeners are informed + foregroundExecutor.advanceClockToLast() + foregroundExecutor.runAllReady() + verify(listener).onMediaDataRemoved(PACKAGE_NAME) + } + + @Test fun testLoadsMetadataOnBackground() { mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.numPending()).isEqualTo(1) @@ -361,7 +387,8 @@ class MediaDataManagerTest : SysuiTestCase() { verify(listener).onSmartspaceMediaDataLoaded( eq(KEY_MEDIA_SMARTSPACE), eq(SmartspaceMediaData(KEY_MEDIA_SMARTSPACE, true /* isActive */, true /*isValid */, - PACKAGE_NAME, null, listOf(mediaRecommendationItem), 0)), + PACKAGE_NAME, mediaSmartspaceBaseAction, listOf(mediaRecommendationItem), + DISMISS_INTENT, 0)), eq(false)) } @@ -372,7 +399,8 @@ class MediaDataManagerTest : SysuiTestCase() { verify(listener).onSmartspaceMediaDataLoaded( eq(KEY_MEDIA_SMARTSPACE), eq(EMPTY_SMARTSPACE_MEDIA_DATA - .copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true, isValid = false)), + .copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true, + isValid = false, dismissIntent = DISMISS_INTENT)), eq(false)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt index 150f4545bd43..359746bca0ff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt @@ -91,10 +91,12 @@ class MediaResumeListenerTest : SysuiTestCase() { @Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback> @Captor lateinit var actionCaptor: ArgumentCaptor<Runnable> + @Captor lateinit var componentCaptor: ArgumentCaptor<String> private lateinit var executor: FakeExecutor private lateinit var data: MediaData private lateinit var resumeListener: MediaResumeListener + private val clock = FakeSystemClock() private var originalQsSetting = Settings.Global.getInt(context.contentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1) @@ -122,9 +124,9 @@ class MediaResumeListenerTest : SysuiTestCase() { whenever(mockContext.packageManager).thenReturn(context.packageManager) whenever(mockContext.contentResolver).thenReturn(context.contentResolver) - executor = FakeExecutor(FakeSystemClock()) + executor = FakeExecutor(clock) resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor, - tunerService, resumeBrowserFactory, dumpManager) + tunerService, resumeBrowserFactory, dumpManager, clock) resumeListener.setManager(mediaDataManager) mediaDataManager.addListener(resumeListener) @@ -163,7 +165,7 @@ class MediaResumeListenerTest : SysuiTestCase() { // When listener is created, we do NOT register a user change listener val listener = MediaResumeListener(context, broadcastDispatcher, executor, tunerService, - resumeBrowserFactory, dumpManager) + resumeBrowserFactory, dumpManager, clock) listener.setManager(mediaDataManager) verify(broadcastDispatcher, never()).registerReceiver(eq(listener.userChangeReceiver), any(), any(), any()) @@ -328,4 +330,109 @@ class MediaResumeListenerTest : SysuiTestCase() { // Then we call restart verify(resumeBrowser).restart() } + + @Test + fun testOnUserUnlock_missingTime_saves() { + val currentTime = clock.currentTimeMillis() + + // When resume components without a last played time are loaded + testOnUserUnlock_loadsTracks() + + // Then we save an update with the current time + verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor))) + componentCaptor.value.split(ResumeMediaBrowser.DELIMITER.toRegex()) + ?.dropLastWhile { it.isEmpty() }.forEach { + val result = it.split("/") + assertThat(result.size).isEqualTo(3) + assertThat(result[2].toLong()).isEqualTo(currentTime) + } + verify(sharedPrefsEditor, times(1)).apply() + } + + @Test + fun testLoadComponents_recentlyPlayed_adds() { + // Set up browser to return successfully + val description = MediaDescription.Builder().setTitle(TITLE).build() + val component = ComponentName(PACKAGE_NAME, CLASS_NAME) + whenever(resumeBrowser.token).thenReturn(token) + whenever(resumeBrowser.appIntent).thenReturn(pendingIntent) + whenever(resumeBrowser.findRecentMedia()).thenAnswer { + callbackCaptor.value.addTrack(description, component, resumeBrowser) + } + + // Set up shared preferences to have a component with a recent lastplayed time + val lastPlayed = clock.currentTimeMillis() + val componentsString = "$PACKAGE_NAME/$CLASS_NAME/$lastPlayed:" + whenever(sharedPrefs.getString(any(), any())).thenReturn(componentsString) + val resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor, + tunerService, resumeBrowserFactory, dumpManager, clock) + resumeListener.setManager(mediaDataManager) + mediaDataManager.addListener(resumeListener) + + // When we load a component that was played recently + val intent = Intent(Intent.ACTION_USER_UNLOCKED) + resumeListener.userChangeReceiver.onReceive(mockContext, intent) + + // We add its resume controls + verify(resumeBrowser, times(1)).findRecentMedia() + verify(mediaDataManager, times(1)).addResumptionControls(anyInt(), + any(), any(), any(), any(), any(), eq(PACKAGE_NAME)) + } + + @Test + fun testLoadComponents_old_ignores() { + // Set up shared preferences to have a component with an old lastplayed time + val lastPlayed = clock.currentTimeMillis() - RESUME_MEDIA_TIMEOUT - 100 + val componentsString = "$PACKAGE_NAME/$CLASS_NAME/$lastPlayed:" + whenever(sharedPrefs.getString(any(), any())).thenReturn(componentsString) + val resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor, + tunerService, resumeBrowserFactory, dumpManager, clock) + resumeListener.setManager(mediaDataManager) + mediaDataManager.addListener(resumeListener) + + // When we load a component that is not recent + val intent = Intent(Intent.ACTION_USER_UNLOCKED) + resumeListener.userChangeReceiver.onReceive(mockContext, intent) + + // We do not try to add resume controls + verify(resumeBrowser, times(0)).findRecentMedia() + verify(mediaDataManager, times(0)).addResumptionControls(anyInt(), + any(), any(), any(), any(), any(), any()) + } + + @Test + fun testOnLoad_hasService_updatesLastPlayed() { + // Set up browser to return successfully + val description = MediaDescription.Builder().setTitle(TITLE).build() + val component = ComponentName(PACKAGE_NAME, CLASS_NAME) + whenever(resumeBrowser.token).thenReturn(token) + whenever(resumeBrowser.appIntent).thenReturn(pendingIntent) + whenever(resumeBrowser.findRecentMedia()).thenAnswer { + callbackCaptor.value.addTrack(description, component, resumeBrowser) + } + + // Set up shared preferences to have a component with a lastplayed time + val currentTime = clock.currentTimeMillis() + val lastPlayed = currentTime - 1000 + val componentsString = "$PACKAGE_NAME/$CLASS_NAME/$lastPlayed:" + whenever(sharedPrefs.getString(any(), any())).thenReturn(componentsString) + val resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor, + tunerService, resumeBrowserFactory, dumpManager, clock) + resumeListener.setManager(mediaDataManager) + mediaDataManager.addListener(resumeListener) + + // When media data is loaded that has not been checked yet, and does have a MBS + val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false) + resumeListener.onMediaDataLoaded(KEY, null, dataCopy) + + // Then we store the new lastPlayed time + verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor))) + componentCaptor.value.split(ResumeMediaBrowser.DELIMITER.toRegex()) + ?.dropLastWhile { it.isEmpty() }.forEach { + val result = it.split("/") + assertThat(result.size).isEqualTo(3) + assertThat(result[2].toLong()).isEqualTo(currentTime) + } + verify(sharedPrefsEditor, times(1)).apply() + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt index 0a573cd6020c..de2235dd1c42 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt @@ -71,6 +71,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() { private lateinit var playbackBuilder: PlaybackState.Builder private lateinit var session: MediaSession private lateinit var mediaData: MediaData + private lateinit var resumeData: MediaData private lateinit var mediaTimeoutListener: MediaTimeoutListener @Before @@ -97,6 +98,10 @@ class MediaTimeoutListenerTest : SysuiTestCase() { mediaData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null, emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null, device = null, active = true, resumeAction = null) + + resumeData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null, + emptyList(), emptyList(), PACKAGE, null, clickIntent = null, + device = null, active = false, resumeAction = null, resumption = true) } @Test @@ -120,6 +125,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() { verify(mediaController).registerCallback(capture(mediaCallbackCaptor)) assertThat(executor.numPending()).isEqualTo(1) verify(timeoutCallback, never()).invoke(anyString(), anyBoolean()) + assertThat(executor.advanceClockToNext()).isEqualTo(PAUSED_MEDIA_TIMEOUT) } @Test @@ -188,6 +194,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() { mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder() .setState(PlaybackState.STATE_PAUSED, 0L, 0f).build()) assertThat(executor.numPending()).isEqualTo(1) + assertThat(executor.advanceClockToNext()).isEqualTo(PAUSED_MEDIA_TIMEOUT) } @Test @@ -229,7 +236,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() { } @Test - fun testOnSessionDestroyed_clearsTimeout() { + fun testOnSessionDestroyed_active_clearsTimeout() { // GIVEN media that is paused val mediaPaused = mediaData.copy(isPlaying = false) mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaPaused) @@ -247,7 +254,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() { @Test fun testSessionDestroyed_thenRestarts_resetsTimeout() { // Assuming we have previously destroyed the session - testOnSessionDestroyed_clearsTimeout() + testOnSessionDestroyed_active_clearsTimeout() // WHEN we get an update with media playing val playingState = mock(android.media.session.PlaybackState::class.java) @@ -264,4 +271,91 @@ class MediaTimeoutListenerTest : SysuiTestCase() { } verify(timeoutCallback).invoke(eq(KEY), eq(false)) } + + @Test + fun testOnSessionDestroyed_resume_continuesTimeout() { + // GIVEN resume media with session info + val resumeWithSession = resumeData.copy(token = session.sessionToken) + mediaTimeoutListener.onMediaDataLoaded(PACKAGE, null, resumeWithSession) + verify(mediaController).registerCallback(capture(mediaCallbackCaptor)) + assertThat(executor.numPending()).isEqualTo(1) + + // WHEN the session is destroyed + mediaCallbackCaptor.value.onSessionDestroyed() + + // THEN the controller is unregistered, but the timeout is still scheduled + verify(mediaController).unregisterCallback(anyObject()) + assertThat(executor.numPending()).isEqualTo(1) + } + + @Test + fun testOnMediaDataLoaded_activeToResume_registersTimeout() { + // WHEN a regular media is loaded + mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData) + + // AND it turns into a resume control + mediaTimeoutListener.onMediaDataLoaded(PACKAGE, KEY, resumeData) + + // THEN we register a timeout + assertThat(executor.numPending()).isEqualTo(1) + verify(timeoutCallback, never()).invoke(anyString(), anyBoolean()) + assertThat(executor.advanceClockToNext()).isEqualTo(RESUME_MEDIA_TIMEOUT) + } + + @Test + fun testOnMediaDataLoaded_pausedToResume_updatesTimeout() { + // WHEN regular media is paused + val pausedState = PlaybackState.Builder() + .setState(PlaybackState.STATE_PAUSED, 0L, 0f) + .build() + `when`(mediaController.playbackState).thenReturn(pausedState) + mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData) + assertThat(executor.numPending()).isEqualTo(1) + + // AND it turns into a resume control + mediaTimeoutListener.onMediaDataLoaded(PACKAGE, KEY, resumeData) + + // THEN we update the timeout length + assertThat(executor.numPending()).isEqualTo(1) + verify(timeoutCallback, never()).invoke(anyString(), anyBoolean()) + assertThat(executor.advanceClockToNext()).isEqualTo(RESUME_MEDIA_TIMEOUT) + } + + @Test + fun testOnMediaDataLoaded_resumption_registersTimeout() { + // WHEN a resume media is loaded + mediaTimeoutListener.onMediaDataLoaded(PACKAGE, null, resumeData) + + // THEN we register a timeout + assertThat(executor.numPending()).isEqualTo(1) + verify(timeoutCallback, never()).invoke(anyString(), anyBoolean()) + assertThat(executor.advanceClockToNext()).isEqualTo(RESUME_MEDIA_TIMEOUT) + } + + @Test + fun testOnMediaDataLoaded_resumeToActive_updatesTimeout() { + // WHEN we have a resume control + mediaTimeoutListener.onMediaDataLoaded(PACKAGE, null, resumeData) + + // AND that media is resumed + val playingState = PlaybackState.Builder() + .setState(PlaybackState.STATE_PAUSED, 0L, 0f) + .build() + `when`(mediaController.playbackState).thenReturn(playingState) + mediaTimeoutListener.onMediaDataLoaded(KEY, PACKAGE, mediaData) + + // THEN the timeout length is changed to a regular media control + assertThat(executor.advanceClockToNext()).isEqualTo(PAUSED_MEDIA_TIMEOUT) + } + + @Test + fun testOnMediaDataRemoved_resume_timeoutCancelled() { + // WHEN we have a resume control + testOnMediaDataLoaded_resumption_registersTimeout() + // AND the media is removed + mediaTimeoutListener.onMediaDataRemoved(PACKAGE) + + // THEN the timeout runnable is cancelled + assertThat(executor.numPending()).isEqualTo(0) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt index 03248f7e3a70..511848d8a3af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt @@ -22,11 +22,13 @@ import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.UserInfo +import android.os.Process.SYSTEM_UID import android.os.UserHandle import android.permission.PermGroupUsage import android.permission.PermissionManager import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest +import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase import com.android.systemui.appops.AppOpsController import com.android.systemui.plugins.ActivityStarter @@ -53,6 +55,7 @@ import org.mockito.Mockito.`when` import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.mock import org.mockito.Mockito.never +import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -96,6 +99,8 @@ class PrivacyDialogControllerTest : SysuiTestCase() { private lateinit var activityStartedCaptor: ArgumentCaptor<ActivityStarter.Callback> @Captor private lateinit var intentCaptor: ArgumentCaptor<Intent> + @Mock + private lateinit var uiEventLogger: UiEventLogger private val backgroundExecutor = FakeExecutor(FakeSystemClock()) private val uiExecutor = FakeExecutor(FakeSystemClock()) @@ -136,6 +141,7 @@ class PrivacyDialogControllerTest : SysuiTestCase() { privacyLogger, keyguardStateController, appOpsController, + uiEventLogger, dialogProvider ) } @@ -550,6 +556,49 @@ class PrivacyDialogControllerTest : SysuiTestCase() { verify(dialog, never()).dismiss() } + @Test + fun testCallOnSecondaryUser() { + // Calls happen in + val usage = createMockPermGroupUsage(uid = SYSTEM_UID, isPhoneCall = true) + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage)) + `when`(userTracker.userProfiles).thenReturn(listOf( + UserInfo(ENT_USER_ID, "", 0) + )) + + controller.showDialog(context) + exhaustExecutors() + + verify(dialog).show() + } + + @Test + fun testStartActivityLogs() { + val usage = createMockPermGroupUsage() + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage)) + controller.showDialog(context) + exhaustExecutors() + + dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID) + verify(uiEventLogger).log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS, + USER_ID, TEST_PACKAGE_NAME) + } + + @Test + fun testDismissedDialogLogs() { + val usage = createMockPermGroupUsage() + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage)) + controller.showDialog(context) + exhaustExecutors() + + verify(dialog).addOnDismissListener(capture(dialogDismissedCaptor)) + + dialogDismissedCaptor.value.onDialogDismissed() + + controller.dismissDialog() + + verify(uiEventLogger, times(1)).log(PrivacyDialogEvent.PRIVACY_DIALOG_DISMISSED) + } + private fun exhaustExecutors() { FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java index de7abf866f6a..922c6b648aab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java @@ -14,13 +14,19 @@ package com.android.systemui.qs; -import static com.android.systemui.statusbar.phone.AutoTileManager.INVERSION; import static com.android.systemui.statusbar.phone.AutoTileManager.SAVER; -import static com.android.systemui.statusbar.phone.AutoTileManager.WORK; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; - +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.content.IntentFilter; import android.os.UserHandle; import android.provider.Settings.Secure; import android.testing.AndroidTestingRunner; @@ -28,13 +34,24 @@ import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; -import com.android.systemui.Prefs; -import com.android.systemui.Prefs.Key; import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.util.settings.FakeSettings; +import com.android.systemui.util.settings.SecureSettings; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.List; +import java.util.concurrent.Executor; @RunWith(AndroidTestingRunner.class) @RunWithLooper @@ -43,42 +60,38 @@ public class AutoAddTrackerTest extends SysuiTestCase { private static final int USER = 0; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private QSHost mQSHost; + @Mock + private DumpManager mDumpManager; + @Captor + private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor; + @Captor + private ArgumentCaptor<IntentFilter> mIntentFilterArgumentCaptor; + + private Executor mBackgroundExecutor = Runnable::run; // Direct executor private AutoAddTracker mAutoTracker; + private SecureSettings mSecureSettings; @Before public void setUp() { - Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, ""); - } + MockitoAnnotations.initMocks(this); - @Test - public void testMigration() { - Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true); - Prefs.putBoolean(mContext, Key.QS_WORK_ADDED, true); - mAutoTracker = new AutoAddTracker(mContext, USER); - mAutoTracker.initialize(); + mSecureSettings = new FakeSettings(); - assertTrue(mAutoTracker.isAdded(SAVER)); - assertTrue(mAutoTracker.isAdded(WORK)); - assertFalse(mAutoTracker.isAdded(INVERSION)); + mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, null, USER); - // These keys have been removed; retrieving their values should always return the default. - assertTrue(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true )); - assertFalse(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, false)); - assertTrue(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, true)); - assertFalse(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, false)); - - mAutoTracker.destroy(); + mAutoTracker = createAutoAddTracker(USER); + mAutoTracker.initialize(); } @Test public void testChangeFromBackup() { - mAutoTracker = new AutoAddTracker(mContext, USER); - mAutoTracker.initialize(); - assertFalse(mAutoTracker.isAdded(SAVER)); - Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, SAVER); - mAutoTracker.mObserver.onChange(false); + mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, SAVER, USER); assertTrue(mAutoTracker.isAdded(SAVER)); @@ -87,9 +100,6 @@ public class AutoAddTrackerTest extends SysuiTestCase { @Test public void testSetAdded() { - mAutoTracker = new AutoAddTracker(mContext, USER); - mAutoTracker.initialize(); - assertFalse(mAutoTracker.isAdded(SAVER)); mAutoTracker.setTileAdded(SAVER); @@ -100,14 +110,12 @@ public class AutoAddTrackerTest extends SysuiTestCase { @Test public void testPersist() { - mAutoTracker = new AutoAddTracker(mContext, USER); - mAutoTracker.initialize(); - assertFalse(mAutoTracker.isAdded(SAVER)); mAutoTracker.setTileAdded(SAVER); mAutoTracker.destroy(); - mAutoTracker = new AutoAddTracker(mContext, USER); + mAutoTracker = createAutoAddTracker(USER); + mAutoTracker.initialize(); assertTrue(mAutoTracker.isAdded(SAVER)); @@ -116,22 +124,158 @@ public class AutoAddTrackerTest extends SysuiTestCase { @Test public void testIndependentUsers() { - mAutoTracker = new AutoAddTracker(mContext, USER); - mAutoTracker.initialize(); mAutoTracker.setTileAdded(SAVER); - mAutoTracker = new AutoAddTracker(mContext, USER + 1); + mAutoTracker = createAutoAddTracker(USER + 1); + mAutoTracker.initialize(); assertFalse(mAutoTracker.isAdded(SAVER)); } @Test public void testChangeUser() { - mAutoTracker = new AutoAddTracker(mContext, USER); - mAutoTracker.initialize(); mAutoTracker.setTileAdded(SAVER); - mAutoTracker = new AutoAddTracker(mContext, USER + 1); + mAutoTracker = createAutoAddTracker(USER + 1); mAutoTracker.changeUser(UserHandle.of(USER)); assertTrue(mAutoTracker.isAdded(SAVER)); } + + @Test + public void testBroadcastReceiverRegistered() { + verify(mBroadcastDispatcher).registerReceiver( + any(), mIntentFilterArgumentCaptor.capture(), any(), eq(UserHandle.of(USER))); + + assertTrue( + mIntentFilterArgumentCaptor.getValue().hasAction(Intent.ACTION_SETTING_RESTORED)); + } + + @Test + public void testBroadcastReceiverChangesWithUser() { + mAutoTracker.changeUser(UserHandle.of(USER + 1)); + + InOrder inOrder = Mockito.inOrder(mBroadcastDispatcher); + inOrder.verify(mBroadcastDispatcher).unregisterReceiver(any()); + inOrder.verify(mBroadcastDispatcher) + .registerReceiver(any(), any(), any(), eq(UserHandle.of(USER + 1))); + } + + @Test + public void testSettingRestoredWithTilesNotRemovedInSource_noAutoAddedInTarget() { + verify(mBroadcastDispatcher).registerReceiver( + mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any()); + + // These tiles were present in the original device + String restoredTiles = "saver,work,internet,cast"; + Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent); + + // And these tiles have been auto-added in the original device + // (no auto-added before restore) + String restoredAutoAddTiles = "work"; + Intent restoreAutoAddTilesIntent = + makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent); + + // Then, don't remove any current tiles + verify(mQSHost, never()).removeTiles(any()); + assertEquals(restoredAutoAddTiles, + mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER)); + } + + @Test + public void testSettingRestoredWithTilesRemovedInSource_noAutoAddedInTarget() { + verify(mBroadcastDispatcher) + .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any()); + + // These tiles were present in the original device + String restoredTiles = "saver,internet,cast"; + Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent); + + // And these tiles have been auto-added in the original device + // (no auto-added before restore) + String restoredAutoAddTiles = "work"; + Intent restoreAutoAddTilesIntent = + makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent); + + // Then, remove work tile + verify(mQSHost).removeTiles(List.of("work")); + assertEquals(restoredAutoAddTiles, + mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER)); + } + + @Test + public void testSettingRestoredWithTilesRemovedInSource_sameAutoAddedinTarget() { + verify(mBroadcastDispatcher) + .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any()); + + // These tiles were present in the original device + String restoredTiles = "saver,internet,cast"; + Intent restoreTilesIntent = + makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent); + + // And these tiles have been auto-added in the original device + // (no auto-added before restore) + String restoredAutoAddTiles = "work"; + Intent restoreAutoAddTilesIntent = + makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "work", restoredAutoAddTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent); + + // Then, remove work tile + verify(mQSHost).removeTiles(List.of("work")); + assertEquals(restoredAutoAddTiles, + mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER)); + } + + @Test + public void testSettingRestoredWithTilesRemovedInSource_othersAutoAddedinTarget() { + verify(mBroadcastDispatcher) + .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any()); + + // These tiles were present in the original device + String restoredTiles = "saver,internet,cast"; + Intent restoreTilesIntent = + makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent); + + // And these tiles have been auto-added in the original device + // (no auto-added before restore) + String restoredAutoAddTiles = "work"; + Intent restoreAutoAddTilesIntent = + makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "inversion", restoredAutoAddTiles); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent); + + // Then, remove work tile + verify(mQSHost).removeTiles(List.of("work")); + + String setting = mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER); + assertEquals(2, setting.split(",").length); + assertTrue(setting.contains("work")); + assertTrue(setting.contains("inversion")); + } + + + private Intent makeRestoreIntent( + String settingName, String previousValue, String restoredValue) { + Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED); + intent.putExtra(Intent.EXTRA_SETTING_NAME, settingName); + intent.putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue); + intent.putExtra(Intent.EXTRA_SETTING_NEW_VALUE, restoredValue); + return intent; + } + + private AutoAddTracker createAutoAddTracker(int user) { + // Null handler wil dispatch sync. + return new AutoAddTracker( + mSecureSettings, + mBroadcastDispatcher, + mQSHost, + mDumpManager, + null, + mBackgroundExecutor, + user + ); + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index 65e5f9703d84..faef87069084 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -98,11 +99,11 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { Resources mResources; @Mock Configuration mConfiguration; + @Mock + Runnable mHorizontalLayoutListener; private QSPanelControllerBase<QSPanel> mController; - - /** Implementation needed to ensure we have a reflectively-available class name. */ private class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> { protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host, @@ -242,18 +243,44 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { when(mMediaHost.getVisible()).thenReturn(true); when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false); + when(mQSPanel.getDumpableTag()).thenReturn("QSPanelLandscape"); mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost, mQSCustomizerController, mMediaHost, mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags); + mController.init(); assertThat(mController.shouldUseHorizontalLayout()).isTrue(); when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true); when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true); + when(mQSPanel.getDumpableTag()).thenReturn("QSPanelPortrait"); mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost, mQSCustomizerController, mMediaHost, mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags); + mController.init(); + + assertThat(mController.shouldUseHorizontalLayout()).isFalse(); + } + + @Test + public void testChangeConfiguration_shouldUseHorizontalLayout() { + when(mMediaHost.getVisible()).thenReturn(true); + mController.setUsingHorizontalLayoutChangeListener(mHorizontalLayoutListener); + + // When device is rotated to landscape + mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE; + mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration); + + // Then the layout changes + assertThat(mController.shouldUseHorizontalLayout()).isTrue(); + verify(mHorizontalLayoutListener).run(); // not invoked + + // When it is rotated back to portrait + mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT; + mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration); + // Then the layout changes back assertThat(mController.shouldUseHorizontalLayout()).isFalse(); + verify(mHorizontalLayoutListener, times(2)).run(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java index bf6c981bf05c..35ebacb85203 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java @@ -23,6 +23,8 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.res.Configuration; +import android.content.res.Resources; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; @@ -98,6 +100,10 @@ public class QSPanelControllerTest extends SysuiTestCase { FalsingManagerFake mFalsingManager = new FalsingManagerFake(); @Mock FeatureFlags mFeatureFlags; + @Mock + Resources mResources; + @Mock + Configuration mConfiguration; private QSPanelController mController; @@ -109,6 +115,8 @@ public class QSPanelControllerTest extends SysuiTestCase { when(mQSPanel.getDumpableTag()).thenReturn("QSPanel"); when(mQSPanel.getOrCreateTileLayout()).thenReturn(mPagedTileLayout); when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout); + when(mQSPanel.getResources()).thenReturn(mResources); + when(mResources.getConfiguration()).thenReturn(mConfiguration); when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile)); when(mQSTileHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView); when(mToggleSliderViewControllerFactory.create(any(), any())) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index 4cbad5f15c5f..0b67c9c51079 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -18,14 +18,11 @@ package com.android.systemui.qs; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import static junit.framework.TestCase.assertFalse; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -68,12 +65,12 @@ import com.android.systemui.statusbar.phone.AutoTileManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.tuner.TunerService; +import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SecureSettings; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -121,7 +118,6 @@ public class QSTileHostTest extends SysuiTestCase { private UiEventLogger mUiEventLogger; @Mock private UserTracker mUserTracker; - @Mock private SecureSettings mSecureSettings; @Mock private CustomTileStatePersister mCustomTileStatePersister; @@ -137,14 +133,16 @@ public class QSTileHostTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mLooper = TestableLooper.get(this); mHandler = new Handler(mLooper.getLooper()); + + mSecureSettings = new FakeSettings(); + mSecureSettings.putStringForUser( + QSTileHost.TILES_SETTING, "", "", false, mUserTracker.getUserId(), false); mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler, mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager, mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister, mFeatureFlags); setUpTileFactory(); when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false); - when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt())) - .thenReturn(""); } private void setUpTileFactory() { @@ -416,6 +414,16 @@ public class QSTileHostTest extends SysuiTestCase { .removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId())); } + @Test + public void testRemoveTiles() { + List<String> tiles = List.of("spec1", "spec2", "spec3"); + mQSTileHost.saveTilesToSettings(tiles); + + mQSTileHost.removeTiles(List.of("spec1", "spec2")); + + assertEquals(List.of("spec3"), mQSTileHost.mTileSpecs); + } + private class TestQSTileHost extends QSTileHost { TestQSTileHost(Context context, StatusBarIconController iconController, QSFactory defaultFactory, Handler mainHandler, Looper bgLooper, @@ -442,14 +450,11 @@ public class QSTileHostTest extends SysuiTestCase { @Override void saveTilesToSettings(List<String> tileSpecs) { super.saveTilesToSettings(tileSpecs); - - ArgumentCaptor<String> specs = ArgumentCaptor.forClass(String.class); - verify(mSecureSettings, atLeastOnce()).putStringForUser(eq(QSTileHost.TILES_SETTING), - specs.capture(), isNull(), eq(false), anyInt(), eq(true)); - // After tiles are changed, make sure to call onTuningChanged with the new setting if it // changed - onTuningChanged(TILES_SETTING, specs.getValue()); + String specs = mSecureSettings.getStringForUser( + QSTileHost.TILES_SETTING, mUserTracker.getUserId()); + onTuningChanged(TILES_SETTING, specs); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt index 35360bd19393..8b7e20ed0e5a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt @@ -36,6 +36,8 @@ import com.android.systemui.statusbar.FeatureFlags import com.android.systemui.statusbar.phone.StatusBarIconController import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.policy.Clock +import com.android.systemui.statusbar.policy.VariableDateView +import com.android.systemui.statusbar.policy.VariableDateViewController import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture @@ -87,8 +89,14 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { @Mock private lateinit var privacyDialogController: PrivacyDialogController @Mock + private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory + @Mock + private lateinit var variableDateViewController: VariableDateViewController + @Mock private lateinit var clock: Clock @Mock + private lateinit var variableDateView: VariableDateView + @Mock private lateinit var mockView: View @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var context: Context @@ -109,6 +117,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { stubViews() `when`(iconContainer.context).thenReturn(context) `when`(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController) + `when`(variableDateViewControllerFactory.create(any())) + .thenReturn(variableDateViewController) `when`(view.resources).thenReturn(mContext.resources) `when`(view.isAttachedToWindow).thenReturn(true) `when`(view.context).thenReturn(context) @@ -133,7 +143,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { colorExtractor, privacyDialogController, qsExpansionPathInterpolator, - featureFlags + featureFlags, + variableDateViewControllerFactory ) } @@ -274,6 +285,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { `when`(view.findViewById<StatusIconContainer>(R.id.statusIcons)).thenReturn(iconContainer) `when`(view.findViewById<OngoingPrivacyChip>(R.id.privacy_chip)).thenReturn(privacyChip) `when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock) + `when`(view.requireViewById<VariableDateView>(R.id.date)).thenReturn(variableDateView) + `when`(view.requireViewById<VariableDateView>(R.id.date_clock)).thenReturn(variableDateView) } private fun setPrivacyController(micCamera: Boolean, location: Boolean) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java index 63ebe9290f64..23e51687f9e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -74,6 +75,24 @@ public class QSIconViewImplTest extends SysuiTestCase { } @Test + public void testMutateIconDrawable() { + SlashImageView iv = mock(SlashImageView.class); + Drawable originalDrawable = mock(Drawable.class); + Drawable otherDrawable = mock(Drawable.class); + State s = new State(); + s.icon = mock(Icon.class); + when(s.icon.getInvisibleDrawable(eq(mContext))).thenReturn(originalDrawable); + when(s.icon.getDrawable(eq(mContext))).thenReturn(originalDrawable); + when(iv.isShown()).thenReturn(true); + when(originalDrawable.getConstantState()).thenReturn(fakeConstantState(otherDrawable)); + + + mIconView.updateIcon(iv, s, /* allowAnimations= */true); + + verify(iv).setState(any(), eq(otherDrawable)); + } + + @Test public void testNoFirstFade() { ImageView iv = mock(ImageView.class); State s = new State(); @@ -104,4 +123,18 @@ public class QSIconViewImplTest extends SysuiTestCase { public void testIconNotSet_toString() { assertFalse(mIconView.toString().contains("lastIcon")); } + + private static Drawable.ConstantState fakeConstantState(Drawable otherDrawable) { + return new Drawable.ConstantState() { + @Override + public Drawable newDrawable() { + return otherDrawable; + } + + @Override + public int getChangingConfigurations() { + return 1; + } + }; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt index 5e2d8fde84da..b4a662974d22 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt @@ -3,6 +3,7 @@ package com.android.systemui.qs.tiles import android.app.AlarmManager import android.app.PendingIntent import android.os.Handler +import android.provider.AlarmClock import android.service.quicksettings.Tile import android.testing.AndroidTestingRunner import android.testing.TestableLooper @@ -98,6 +99,11 @@ class AlarmTileTest : SysuiTestCase() { } @Test + fun testDefaultIntentShowAlarms() { + assertThat(tile.defaultIntent.action).isEqualTo(AlarmClock.ACTION_SHOW_ALARMS) + } + + @Test fun testInactiveByDefault() { assertThat(tile.state.state).isEqualTo(Tile.STATE_INACTIVE) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java index d44a52607707..e939411e4a2a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertEquals; +import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; @@ -327,4 +328,77 @@ public class CastTileTest extends SysuiTestCase { assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state); assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(connected.name)); } + + @Test + public void testExpandView_wifiNotConnected() { + mCastTile.refreshState(); + mTestableLooper.processAllMessages(); + + assertFalse(mCastTile.getState().forceExpandIcon); + } + + @Test + public void testExpandView_wifiEnabledNotCasting() { + enableWifiAndProcessMessages(); + + assertTrue(mCastTile.getState().forceExpandIcon); + } + + @Test + public void testExpandView_casting_projection() { + CastController.CastDevice device = new CastController.CastDevice(); + device.state = CastController.CastDevice.STATE_CONNECTED; + List<CastDevice> devices = new ArrayList<>(); + devices.add(device); + when(mController.getCastDevices()).thenReturn(devices); + + enableWifiAndProcessMessages(); + + assertFalse(mCastTile.getState().forceExpandIcon); + } + + @Test + public void testExpandView_connecting_projection() { + CastController.CastDevice connecting = new CastController.CastDevice(); + connecting.state = CastDevice.STATE_CONNECTING; + connecting.name = "Test Casting Device"; + + List<CastDevice> devices = new ArrayList<>(); + devices.add(connecting); + when(mController.getCastDevices()).thenReturn(devices); + + enableWifiAndProcessMessages(); + + assertFalse(mCastTile.getState().forceExpandIcon); + } + + @Test + public void testExpandView_casting_mediaRoute() { + CastController.CastDevice device = new CastController.CastDevice(); + device.state = CastDevice.STATE_CONNECTED; + device.tag = mock(MediaRouter.RouteInfo.class); + List<CastDevice> devices = new ArrayList<>(); + devices.add(device); + when(mController.getCastDevices()).thenReturn(devices); + + enableWifiAndProcessMessages(); + + assertTrue(mCastTile.getState().forceExpandIcon); + } + + @Test + public void testExpandView_connecting_mediaRoute() { + CastController.CastDevice connecting = new CastController.CastDevice(); + connecting.state = CastDevice.STATE_CONNECTING; + connecting.tag = mock(RouteInfo.class); + connecting.name = "Test Casting Device"; + + List<CastDevice> devices = new ArrayList<>(); + devices.add(connecting); + when(mController.getCastDevices()).thenReturn(devices); + + enableWifiAndProcessMessages(); + + assertTrue(mCastTile.getState().forceExpandIcon); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java new file mode 100644 index 000000000000..49ab777624e3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2021 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.qs.tiles; + +import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; +import static android.provider.Settings.Secure.CAMERA_AUTOROTATE; + +import static junit.framework.TestCase.assertEquals; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; + +import android.Manifest; +import android.content.pm.PackageManager; +import android.hardware.SensorPrivacyManager; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.text.TextUtils; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSTileHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.RotationLockController; +import com.android.systemui.statusbar.policy.RotationLockControllerImpl; +import com.android.systemui.util.settings.FakeSettings; +import com.android.systemui.util.settings.SecureSettings; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +public class RotationLockTileTest extends SysuiTestCase { + + private static final String PACKAGE_NAME = "package_name"; + + @Mock + private PackageManager mPackageManager; + @Mock + private ActivityStarter mActivityStarter; + @Mock + private QSTileHost mHost; + @Mock + private MetricsLogger mMetricsLogger; + @Mock + private StatusBarStateController mStatusBarStateController; + @Mock + private QSLogger mQSLogger; + @Mock + private SensorPrivacyManager mPrivacyManager; + @Mock + private BatteryController mBatteryController; + + private SecureSettings mSecureSettings; + private RotationLockController mController; + private TestableLooper mTestableLooper; + private RotationLockTile mLockTile; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mTestableLooper = TestableLooper.get(this); + + when(mHost.getContext()).thenReturn(mContext); + when(mHost.getUserContext()).thenReturn(mContext); + + mSecureSettings = new FakeSettings(); + mController = new RotationLockControllerImpl(mContext, mSecureSettings); + + mLockTile = new RotationLockTile( + mHost, + mTestableLooper.getLooper(), + new Handler(mTestableLooper.getLooper()), + new FalsingManagerFake(), + mMetricsLogger, + mStatusBarStateController, + mActivityStarter, + mQSLogger, + mController, + mPrivacyManager, + mBatteryController, + mSecureSettings + ); + + mLockTile.initialize(); + + // We are not setting the mocks to listening, so we trigger a first refresh state to + // set the initial state + mLockTile.refreshState(); + + mTestableLooper.processAllMessages(); + + mContext.setMockPackageManager(mPackageManager); + doReturn(PACKAGE_NAME).when(mPackageManager).getRotationResolverPackageName(); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPackageManager).checkPermission( + Manifest.permission.CAMERA, PACKAGE_NAME); + + when(mBatteryController.isPowerSave()).thenReturn(false); + when(mPrivacyManager.isSensorPrivacyEnabled(CAMERA)).thenReturn(false); + enableAutoRotation(); + enableCameraBasedRotation(); + + mLockTile.refreshState(); + mTestableLooper.processAllMessages(); + } + + @Test + public void testSecondaryString_cameraRotateOn_returnsFaceBased() { + assertEquals("On - Face-based", mLockTile.getState().secondaryLabel); + } + + @Test + public void testSecondaryString_rotateOff_isEmpty() { + disableAutoRotation(); + + mLockTile.refreshState(); + mTestableLooper.processAllMessages(); + + assertTrue(TextUtils.isEmpty(mLockTile.getState().secondaryLabel)); + } + + @Test + public void testSecondaryString_cameraRotateOff_isEmpty() { + disableCameraBasedRotation(); + + mLockTile.refreshState(); + mTestableLooper.processAllMessages(); + + assertTrue(TextUtils.isEmpty(mLockTile.getState().secondaryLabel)); + } + + @Test + public void testSecondaryString_powerSaveEnabled_isEmpty() { + when(mBatteryController.isPowerSave()).thenReturn(true); + + mLockTile.refreshState(); + mTestableLooper.processAllMessages(); + + assertTrue(TextUtils.isEmpty(mLockTile.getState().secondaryLabel)); + } + + @Test + public void testSecondaryString_cameraDisabled_isEmpty() { + when(mPrivacyManager.isSensorPrivacyEnabled(CAMERA)).thenReturn(true); + + mLockTile.refreshState(); + mTestableLooper.processAllMessages(); + + assertTrue(TextUtils.isEmpty(mLockTile.getState().secondaryLabel)); + } + + @Test + public void testSecondaryString_noCameraPermission_isEmpty() { + doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission( + Manifest.permission.CAMERA, PACKAGE_NAME); + + mLockTile.refreshState(); + mTestableLooper.processAllMessages(); + + assertTrue(TextUtils.isEmpty(mLockTile.getState().secondaryLabel)); + } + + private void enableAutoRotation() { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.ACCELEROMETER_ROTATION, 1, UserHandle.USER_CURRENT); + } + + private void disableAutoRotation() { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT); + } + + private void enableCameraBasedRotation() { + mSecureSettings.putIntForUser( + CAMERA_AUTOROTATE, 1, UserHandle.USER_CURRENT); + } + + private void disableCameraBasedRotation() { + mSecureSettings.putIntForUser( + CAMERA_AUTOROTATE, 0, UserHandle.USER_CURRENT); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java new file mode 100644 index 000000000000..77946cf791aa --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java @@ -0,0 +1,167 @@ +package com.android.systemui.qs.tiles.dialog; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.graphics.drawable.Drawable; +import android.testing.AndroidTestingRunner; +import android.testing.TestableResources; +import android.view.View; +import android.widget.LinearLayout; + +import androidx.test.filters.SmallTest; + +import com.android.settingslib.wifi.WifiUtils; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.wifitrackerlib.WifiEntry; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.Arrays; +import java.util.List; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class InternetAdapterTest extends SysuiTestCase { + + private static final String WIFI_TITLE = "Wi-Fi Title"; + private static final String WIFI_SUMMARY = "Wi-Fi Summary"; + private static final int GEAR_ICON_RES_ID = R.drawable.ic_settings_24dp; + private static final int LOCK_ICON_RES_ID = R.drawable.ic_friction_lock_closed; + + @Rule + public MockitoRule mRule = MockitoJUnit.rule(); + + @Mock + private WifiEntry mInternetWifiEntry; + @Mock + private List<WifiEntry> mWifiEntries; + @Mock + private WifiEntry mWifiEntry; + @Mock + private InternetDialogController mInternetDialogController; + @Mock + private WifiUtils.InternetIconInjector mWifiIconInjector; + @Mock + private Drawable mGearIcon; + @Mock + private Drawable mLockIcon; + + private TestableResources mTestableResources; + private InternetAdapter mInternetAdapter; + private InternetAdapter.InternetViewHolder mViewHolder; + + @Before + public void setUp() { + mTestableResources = mContext.getOrCreateTestableResources(); + when(mInternetWifiEntry.getTitle()).thenReturn(WIFI_TITLE); + when(mInternetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY); + when(mInternetWifiEntry.isDefaultNetwork()).thenReturn(true); + when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true); + when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE); + when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY); + + mInternetAdapter = new InternetAdapter(mInternetDialogController); + mViewHolder = mInternetAdapter.onCreateViewHolder(new LinearLayout(mContext), 0); + mInternetAdapter.setWifiEntries(Arrays.asList(mWifiEntry), 1 /* wifiEntriesCount */); + mViewHolder.mWifiIconInjector = mWifiIconInjector; + } + + @Test + public void getItemCount_returnWifiEntriesCount() { + for (int i = 0; i < InternetDialogController.MAX_WIFI_ENTRY_COUNT; i++) { + mInternetAdapter.setWifiEntries(mWifiEntries, i /* wifiEntriesCount */); + + assertThat(mInternetAdapter.getItemCount()).isEqualTo(i); + } + } + + @Test + public void onBindViewHolder_bindWithOpenWifiNetwork_verifyView() { + when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_NONE); + mInternetAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mWifiTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mWifiSummaryText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mWifiIcon.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mWifiEndIcon.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void onBindViewHolder_bindWithSecurityWifiNetwork_verifyView() { + when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_PSK); + mInternetAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mWifiTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mWifiSummaryText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mWifiIcon.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mWifiEndIcon.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void onBindViewHolder_wifiLevelUnreachable_shouldNotGetWifiIcon() { + reset(mWifiIconInjector); + when(mWifiEntry.getLevel()).thenReturn(WifiEntry.WIFI_LEVEL_UNREACHABLE); + + mInternetAdapter.onBindViewHolder(mViewHolder, 0); + + verify(mWifiIconInjector, never()).getIcon(anyBoolean(), anyInt()); + } + + @Test + public void onBindViewHolder_shouldNotShowXLevelIcon_getIconWithInternet() { + when(mWifiEntry.shouldShowXLevelIcon()).thenReturn(false); + + mInternetAdapter.onBindViewHolder(mViewHolder, 0); + + verify(mWifiIconInjector).getIcon(eq(false) /* noInternet */, anyInt()); + } + + @Test + public void onBindViewHolder_shouldShowXLevelIcon_getIconWithNoInternet() { + when(mWifiEntry.shouldShowXLevelIcon()).thenReturn(true); + + mInternetAdapter.onBindViewHolder(mViewHolder, 0); + + verify(mWifiIconInjector).getIcon(eq(true) /* noInternet */, anyInt()); + } + + @Test + public void viewHolderUpdateEndIcon_wifiConnected_updateGearIcon() { + mTestableResources.addOverride(GEAR_ICON_RES_ID, mGearIcon); + + mViewHolder.updateEndIcon(WifiEntry.CONNECTED_STATE_CONNECTED, WifiEntry.SECURITY_PSK); + + assertThat(mViewHolder.mWifiEndIcon.getDrawable()).isEqualTo(mGearIcon); + } + + @Test + public void viewHolderUpdateEndIcon_wifiDisconnectedAndSecurityPsk_updateLockIcon() { + mTestableResources.addOverride(LOCK_ICON_RES_ID, mLockIcon); + + mViewHolder.updateEndIcon(WifiEntry.CONNECTED_STATE_DISCONNECTED, WifiEntry.SECURITY_PSK); + + assertThat(mViewHolder.mWifiEndIcon.getDrawable()).isEqualTo(mLockIcon); + } + + @Test + public void viewHolderUpdateEndIcon_wifiDisconnectedAndSecurityNone_hideIcon() { + mViewHolder.updateEndIcon(WifiEntry.CONNECTED_STATE_DISCONNECTED, WifiEntry.SECURITY_NONE); + + assertThat(mViewHolder.mWifiEndIcon.getVisibility()).isEqualTo(View.GONE); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java new file mode 100644 index 000000000000..663edc7b373b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java @@ -0,0 +1,675 @@ +package com.android.systemui.qs.tiles.dialog; + +import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_HORIZONTAL_WEIGHT; +import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_VERTICAL_WEIGHT; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.animation.Animator; +import android.content.Context; +import android.content.Intent; +import android.graphics.PixelFormat; +import android.graphics.drawable.Drawable; +import android.net.ConnectivityManager; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.telephony.ServiceState; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableResources; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; +import android.view.WindowManager; + +import androidx.annotation.Nullable; +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.UiEventLogger; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.settingslib.wifi.WifiUtils; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.LocationController; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkController.AccessPointController; +import com.android.systemui.toast.SystemUIToast; +import com.android.systemui.toast.ToastFactory; +import com.android.systemui.util.CarrierConfigTracker; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.settings.GlobalSettings; +import com.android.systemui.util.time.FakeSystemClock; +import com.android.wifitrackerlib.MergedCarrierEntry; +import com.android.wifitrackerlib.WifiEntry; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class InternetDialogControllerTest extends SysuiTestCase { + + private static final int SUB_ID = 1; + private static final String CONNECTED_TITLE = "Connected Wi-Fi Title"; + private static final String CONNECTED_SUMMARY = "Connected Wi-Fi Summary"; + + //SystemUIToast + private static final int GRAVITY_FLAGS = Gravity.FILL_HORIZONTAL | Gravity.FILL_VERTICAL; + private static final int TOAST_MESSAGE_STRING_ID = 1; + private static final String TOAST_MESSAGE_STRING = "toast message"; + + @Mock + private WifiManager mWifiManager; + @Mock + private TelephonyManager mTelephonyManager; + @Mock + private SubscriptionManager mSubscriptionManager; + @Mock + private Handler mHandler; + @Mock + private Handler mWorkerHandler; + @Mock + private ActivityStarter mActivityStarter; + @Mock + private GlobalSettings mGlobalSettings; + @Mock + private KeyguardStateController mKeyguardStateController; + @Mock + private NetworkController.AccessPointController mAccessPointController; + @Mock + private WifiEntry mConnectedEntry; + @Mock + private WifiEntry mWifiEntry1; + @Mock + private WifiEntry mWifiEntry2; + @Mock + private WifiEntry mWifiEntry3; + @Mock + private WifiEntry mWifiEntry4; + @Mock + private MergedCarrierEntry mMergedCarrierEntry; + @Mock + private ServiceState mServiceState; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private WifiUtils.InternetIconInjector mWifiIconInjector; + @Mock + InternetDialogController.InternetDialogCallback mInternetDialogCallback; + @Mock + private WindowManager mWindowManager; + @Mock + private ToastFactory mToastFactory; + @Mock + private SystemUIToast mSystemUIToast; + @Mock + private View mToastView; + @Mock + private Animator mAnimator; + @Mock + private CarrierConfigTracker mCarrierConfigTracker; + @Mock + private LocationController mLocationController; + + private TestableResources mTestableResources; + private MockInternetDialogController mInternetDialogController; + private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + private List<WifiEntry> mAccessPoints = new ArrayList<>(); + private List<WifiEntry> mWifiEntries = new ArrayList<>(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mTestableResources = mContext.getOrCreateTestableResources(); + doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt()); + when(mKeyguardStateController.isUnlocked()).thenReturn(true); + when(mConnectedEntry.isDefaultNetwork()).thenReturn(true); + when(mConnectedEntry.hasInternetAccess()).thenReturn(true); + when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED); + when(mWifiEntry2.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED); + when(mWifiEntry3.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED); + when(mWifiEntry4.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED); + mAccessPoints.add(mConnectedEntry); + mAccessPoints.add(mWifiEntry1); + when(mAccessPointController.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry); + when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{SUB_ID}); + when(mAccessPointController.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry); + when(mToastFactory.createToast(any(), anyString(), anyString(), anyInt(), anyInt())) + .thenReturn(mSystemUIToast); + when(mSystemUIToast.getView()).thenReturn(mToastView); + when(mSystemUIToast.getGravity()).thenReturn(GRAVITY_FLAGS); + when(mSystemUIToast.getInAnimation()).thenReturn(mAnimator); + + mInternetDialogController = new MockInternetDialogController(mContext, + mock(UiEventLogger.class), mock(ActivityStarter.class), mAccessPointController, + mSubscriptionManager, mTelephonyManager, mWifiManager, + mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher, + mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController, + mWindowManager, mToastFactory, mWorkerHandler, mCarrierConfigTracker, + mLocationController); + mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, + mInternetDialogController.mOnSubscriptionsChangedListener); + mInternetDialogController.onStart(mInternetDialogCallback, true); + mInternetDialogController.onAccessPointsChanged(mAccessPoints); + mInternetDialogController.mActivityStarter = mActivityStarter; + mInternetDialogController.mWifiIconInjector = mWifiIconInjector; + } + + @Test + public void connectCarrierNetwork_mergedCarrierEntryCanConnect_connectAndCreateSysUiToast() { + when(mMergedCarrierEntry.canConnect()).thenReturn(true); + mTestableResources.addOverride(R.string.wifi_wont_autoconnect_for_now, + TOAST_MESSAGE_STRING); + + mInternetDialogController.connectCarrierNetwork(); + + verify(mMergedCarrierEntry).connect(null /* callback */, false /* showToast */); + verify(mToastFactory).createToast(any(), eq(TOAST_MESSAGE_STRING), anyString(), anyInt(), + anyInt()); + } + + @Test + public void makeOverlayToast_withGravityFlags_addViewWithLayoutParams() { + mTestableResources.addOverride(TOAST_MESSAGE_STRING_ID, TOAST_MESSAGE_STRING); + + mInternetDialogController.makeOverlayToast(TOAST_MESSAGE_STRING_ID); + + ArgumentCaptor<WindowManager.LayoutParams> paramsCaptor = ArgumentCaptor.forClass( + WindowManager.LayoutParams.class); + verify(mWindowManager).addView(eq(mToastView), paramsCaptor.capture()); + WindowManager.LayoutParams params = paramsCaptor.getValue(); + assertThat(params.format).isEqualTo(PixelFormat.TRANSLUCENT); + assertThat(params.type).isEqualTo(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); + assertThat(params.horizontalWeight).isEqualTo(TOAST_PARAMS_HORIZONTAL_WEIGHT); + assertThat(params.verticalWeight).isEqualTo(TOAST_PARAMS_VERTICAL_WEIGHT); + } + + @Test + public void makeOverlayToast_withAnimation_verifyAnimatorStart() { + mTestableResources.addOverride(TOAST_MESSAGE_STRING_ID, TOAST_MESSAGE_STRING); + + mInternetDialogController.makeOverlayToast(TOAST_MESSAGE_STRING_ID); + + verify(mAnimator).start(); + } + + @Test + public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() { + mInternetDialogController.setAirplaneModeEnabled(true); + + assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(), + getResourcesString("airplane_mode"))); + } + + @Test + public void getDialogTitleText_withAirplaneModeOff_returnInternet() { + mInternetDialogController.setAirplaneModeEnabled(false); + + assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(), + getResourcesString("quick_settings_internet_label"))); + } + + @Test + public void getSubtitleText_withAirplaneModeOn_returnNull() { + mInternetDialogController.setAirplaneModeEnabled(true); + + assertThat(mInternetDialogController.getSubtitleText(false)).isNull(); + } + + @Test + public void getSubtitleText_withWifiOff_returnWifiIsOff() { + mInternetDialogController.setAirplaneModeEnabled(false); + when(mWifiManager.isWifiEnabled()).thenReturn(false); + + assertThat(mInternetDialogController.getSubtitleText(false)) + .isEqualTo(getResourcesString("wifi_is_off")); + + // if the Wi-Fi disallow config, then don't return Wi-Fi related string. + mInternetDialogController.mCanConfigWifi = false; + + assertThat(mInternetDialogController.getSubtitleText(false)) + .isNotEqualTo(getResourcesString("wifi_is_off")); + } + + @Test + public void getSubtitleText_withNoWifiEntry_returnSearchWifi() { + mInternetDialogController.setAirplaneModeEnabled(false); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); + + assertThat(mInternetDialogController.getSubtitleText(true)) + .isEqualTo(getResourcesString("wifi_empty_list_wifi_on")); + + // if the Wi-Fi disallow config, then don't return Wi-Fi related string. + mInternetDialogController.mCanConfigWifi = false; + + assertThat(mInternetDialogController.getSubtitleText(true)) + .isNotEqualTo(getResourcesString("wifi_empty_list_wifi_on")); + } + + @Test + public void getSubtitleText_withWifiEntry_returnTapToConnect() { + // The preconditions WiFi Entries is already in setUp() + mInternetDialogController.setAirplaneModeEnabled(false); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + + assertThat(mInternetDialogController.getSubtitleText(false)) + .isEqualTo(getResourcesString("tap_a_network_to_connect")); + + // if the Wi-Fi disallow config, then don't return Wi-Fi related string. + mInternetDialogController.mCanConfigWifi = false; + + assertThat(mInternetDialogController.getSubtitleText(false)) + .isNotEqualTo(getResourcesString("tap_a_network_to_connect")); + } + + @Test + public void getSubtitleText_deviceLockedWithWifiOn_returnUnlockToViewNetworks() { + mInternetDialogController.setAirplaneModeEnabled(false); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + when(mKeyguardStateController.isUnlocked()).thenReturn(false); + + assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false), + getResourcesString("unlock_to_view_networks"))); + } + + @Test + public void getSubtitleText_withNoService_returnNoNetworksAvailable() { + mInternetDialogController.setAirplaneModeEnabled(false); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); + + doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState(); + doReturn(mServiceState).when(mTelephonyManager).getServiceState(); + doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState(); + + assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false), + getResourcesString("all_network_unavailable"))); + } + + @Test + public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() { + mInternetDialogController.setAirplaneModeEnabled(false); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); + + doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState(); + doReturn(mServiceState).when(mTelephonyManager).getServiceState(); + + when(mTelephonyManager.isDataEnabled()).thenReturn(false); + + assertThat(mInternetDialogController.getSubtitleText(false)) + .isEqualTo(getResourcesString("non_carrier_network_unavailable")); + + // if the Wi-Fi disallow config, then don't return Wi-Fi related string. + mInternetDialogController.mCanConfigWifi = false; + + assertThat(mInternetDialogController.getSubtitleText(false)) + .isNotEqualTo(getResourcesString("non_carrier_network_unavailable")); + } + + @Test + public void getWifiDetailsSettingsIntent_withNoKey_returnNull() { + assertThat(mInternetDialogController.getWifiDetailsSettingsIntent(null)).isNull(); + } + + @Test + public void getWifiDetailsSettingsIntent_withKey_returnIntent() { + assertThat(mInternetDialogController.getWifiDetailsSettingsIntent("test_key")).isNotNull(); + } + + @Test + public void getInternetWifiDrawable_withConnectedEntry_returnIntentIconWithCorrectColor() { + final Drawable drawable = mock(Drawable.class); + when(mWifiIconInjector.getIcon(anyBoolean(), anyInt())).thenReturn(drawable); + + mInternetDialogController.getInternetWifiDrawable(mConnectedEntry); + + verify(mWifiIconInjector).getIcon(eq(false), anyInt()); + verify(drawable).setTint(mContext.getColor(R.color.connected_network_primary_color)); + } + + @Test + public void getInternetWifiDrawable_withWifiLevelUnreachable_returnNull() { + when(mConnectedEntry.getLevel()).thenReturn(WifiEntry.WIFI_LEVEL_UNREACHABLE); + + Drawable drawable = mInternetDialogController.getInternetWifiDrawable(mConnectedEntry); + + assertThat(drawable).isNull(); + } + + @Test + public void launchWifiNetworkDetailsSetting_withNoWifiEntryKey_doNothing() { + mInternetDialogController.launchWifiNetworkDetailsSetting(null /* key */); + + verify(mActivityStarter, never()) + .postStartActivityDismissingKeyguard(any(Intent.class), anyInt()); + } + + @Test + public void launchWifiNetworkDetailsSetting_withWifiEntryKey_startActivity() { + mInternetDialogController.launchWifiNetworkDetailsSetting("wifi_entry_key"); + + verify(mActivityStarter).postStartActivityDismissingKeyguard(any(Intent.class), anyInt()); + } + + @Test + public void isDeviceLocked_keyguardIsUnlocked_returnFalse() { + when(mKeyguardStateController.isUnlocked()).thenReturn(true); + + assertThat(mInternetDialogController.isDeviceLocked()).isFalse(); + } + + @Test + public void isDeviceLocked_keyguardIsLocked_returnTrue() { + when(mKeyguardStateController.isUnlocked()).thenReturn(false); + + assertThat(mInternetDialogController.isDeviceLocked()).isTrue(); + } + + @Test + public void onAccessPointsChanged_canNotConfigWifi_doNothing() { + reset(mInternetDialogCallback); + mInternetDialogController.mCanConfigWifi = false; + + mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); + + verify(mInternetDialogCallback, never()).onAccessPointsChanged(any(), any()); + } + + @Test + public void onAccessPointsChanged_nullAccessPoints_callbackBothNull() { + reset(mInternetDialogCallback); + + mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); + + verify(mInternetDialogCallback) + .onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */); + } + + @Test + public void onAccessPointsChanged_oneConnectedEntry_callbackConnectedEntryOnly() { + reset(mInternetDialogCallback); + mInternetDialogController.setAirplaneModeEnabled(true); + mAccessPoints.clear(); + mAccessPoints.add(mConnectedEntry); + + mInternetDialogController.onAccessPointsChanged(mAccessPoints); + + mWifiEntries.clear(); + verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry); + } + + @Test + public void onAccessPointsChanged_noConnectedEntryAndOneOther_callbackWifiEntriesOnly() { + reset(mInternetDialogCallback); + mInternetDialogController.setAirplaneModeEnabled(true); + mAccessPoints.clear(); + mAccessPoints.add(mWifiEntry1); + + mInternetDialogController.onAccessPointsChanged(mAccessPoints); + + mWifiEntries.clear(); + mWifiEntries.add(mWifiEntry1); + verify(mInternetDialogCallback) + .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */); + } + + @Test + public void onAccessPointsChanged_oneConnectedEntryAndOneOther_callbackCorrectly() { + reset(mInternetDialogCallback); + mInternetDialogController.setAirplaneModeEnabled(true); + mAccessPoints.clear(); + mAccessPoints.add(mConnectedEntry); + mAccessPoints.add(mWifiEntry1); + + mInternetDialogController.onAccessPointsChanged(mAccessPoints); + + mWifiEntries.clear(); + mWifiEntries.add(mWifiEntry1); + verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry); + } + + @Test + public void onAccessPointsChanged_oneConnectedEntryAndTwoOthers_callbackCorrectly() { + reset(mInternetDialogCallback); + mInternetDialogController.setAirplaneModeEnabled(true); + mAccessPoints.clear(); + mAccessPoints.add(mConnectedEntry); + mAccessPoints.add(mWifiEntry1); + mAccessPoints.add(mWifiEntry2); + + mInternetDialogController.onAccessPointsChanged(mAccessPoints); + + mWifiEntries.clear(); + mWifiEntries.add(mWifiEntry1); + mWifiEntries.add(mWifiEntry2); + verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry); + } + + @Test + public void onAccessPointsChanged_oneConnectedEntryAndThreeOthers_callbackCutMore() { + reset(mInternetDialogCallback); + mInternetDialogController.setAirplaneModeEnabled(true); + mAccessPoints.clear(); + mAccessPoints.add(mConnectedEntry); + mAccessPoints.add(mWifiEntry1); + mAccessPoints.add(mWifiEntry2); + mAccessPoints.add(mWifiEntry3); + + mInternetDialogController.onAccessPointsChanged(mAccessPoints); + + mWifiEntries.clear(); + mWifiEntries.add(mWifiEntry1); + mWifiEntries.add(mWifiEntry2); + mWifiEntries.add(mWifiEntry3); + verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry); + + // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one. + reset(mInternetDialogCallback); + mInternetDialogController.setAirplaneModeEnabled(false); + + mInternetDialogController.onAccessPointsChanged(mAccessPoints); + + mWifiEntries.remove(mWifiEntry3); + verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry); + } + + @Test + public void onAccessPointsChanged_oneConnectedEntryAndFourOthers_callbackCutMore() { + reset(mInternetDialogCallback); + mInternetDialogController.setAirplaneModeEnabled(true); + mAccessPoints.clear(); + mAccessPoints.add(mConnectedEntry); + mAccessPoints.add(mWifiEntry1); + mAccessPoints.add(mWifiEntry2); + mAccessPoints.add(mWifiEntry3); + mAccessPoints.add(mWifiEntry4); + + mInternetDialogController.onAccessPointsChanged(mAccessPoints); + + mWifiEntries.clear(); + mWifiEntries.add(mWifiEntry1); + mWifiEntries.add(mWifiEntry2); + mWifiEntries.add(mWifiEntry3); + verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry); + + // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one. + reset(mInternetDialogCallback); + mInternetDialogController.setAirplaneModeEnabled(false); + + mInternetDialogController.onAccessPointsChanged(mAccessPoints); + + mWifiEntries.remove(mWifiEntry3); + verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry); + } + + @Test + public void onAccessPointsChanged_fourWifiEntries_callbackCutMore() { + reset(mInternetDialogCallback); + mInternetDialogController.setAirplaneModeEnabled(true); + mAccessPoints.clear(); + mAccessPoints.add(mWifiEntry1); + mAccessPoints.add(mWifiEntry2); + mAccessPoints.add(mWifiEntry3); + mAccessPoints.add(mWifiEntry4); + + mInternetDialogController.onAccessPointsChanged(mAccessPoints); + + mWifiEntries.clear(); + mWifiEntries.add(mWifiEntry1); + mWifiEntries.add(mWifiEntry2); + mWifiEntries.add(mWifiEntry3); + mWifiEntries.add(mWifiEntry4); + verify(mInternetDialogCallback) + .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */); + + // If the Ethernet exists, then Wi-Fi entries will cut last one. + reset(mInternetDialogCallback); + mInternetDialogController.mHasEthernet = true; + + mInternetDialogController.onAccessPointsChanged(mAccessPoints); + + mWifiEntries.remove(mWifiEntry4); + verify(mInternetDialogCallback) + .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */); + + // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one. + reset(mInternetDialogCallback); + mInternetDialogController.setAirplaneModeEnabled(false); + + mInternetDialogController.onAccessPointsChanged(mAccessPoints); + + mWifiEntries.remove(mWifiEntry3); + verify(mInternetDialogCallback) + .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */); + } + + @Test + public void setMergedCarrierWifiEnabledIfNeed_carrierProvisionsEnabled_doNothing() { + when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID)) + .thenReturn(true); + + mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true); + + verify(mMergedCarrierEntry, never()).setEnabled(anyBoolean()); + } + + @Test + public void setMergedCarrierWifiEnabledIfNeed_mergedCarrierEntryEmpty_doesntCrash() { + when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID)) + .thenReturn(false); + when(mAccessPointController.getMergedCarrierEntry()).thenReturn(null); + + mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true); + } + + @Test + public void setMergedCarrierWifiEnabledIfNeed_neededSetMergedCarrierEntry_setTogether() { + when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID)) + .thenReturn(false); + + mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true); + + verify(mMergedCarrierEntry).setEnabled(true); + + mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, false); + + verify(mMergedCarrierEntry).setEnabled(false); + } + + @Test + public void isWifiScanEnabled_locationOff_returnFalse() { + when(mLocationController.isLocationEnabled()).thenReturn(false); + when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false); + + assertThat(mInternetDialogController.isWifiScanEnabled()).isFalse(); + + when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true); + + assertThat(mInternetDialogController.isWifiScanEnabled()).isFalse(); + } + + @Test + public void isWifiScanEnabled_locationOn_returnIsScanAlwaysAvailable() { + when(mLocationController.isLocationEnabled()).thenReturn(true); + when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false); + + assertThat(mInternetDialogController.isWifiScanEnabled()).isFalse(); + + when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true); + + assertThat(mInternetDialogController.isWifiScanEnabled()).isTrue(); + } + + private String getResourcesString(String name) { + return mContext.getResources().getString(getResourcesId(name)); + } + + private int getResourcesId(String name) { + return mContext.getResources().getIdentifier(name, "string", + mContext.getPackageName()); + } + + private class MockInternetDialogController extends InternetDialogController { + + private GlobalSettings mGlobalSettings; + private boolean mIsAirplaneModeOn; + + MockInternetDialogController(Context context, UiEventLogger uiEventLogger, + ActivityStarter starter, AccessPointController accessPointController, + SubscriptionManager subscriptionManager, TelephonyManager telephonyManager, + @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager, + @Main Handler handler, @Main Executor mainExecutor, + BroadcastDispatcher broadcastDispatcher, + KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings, + KeyguardStateController keyguardStateController, WindowManager windowManager, + ToastFactory toastFactory, Handler workerHandler, + CarrierConfigTracker carrierConfigTracker, + LocationController locationController) { + super(context, uiEventLogger, starter, accessPointController, subscriptionManager, + telephonyManager, wifiManager, connectivityManager, handler, mainExecutor, + broadcastDispatcher, keyguardUpdateMonitor, globalSettings, + keyguardStateController, windowManager, toastFactory, workerHandler, + carrierConfigTracker, locationController); + mGlobalSettings = globalSettings; + } + + @Override + boolean isAirplaneModeEnabled() { + return mIsAirplaneModeOn; + } + + public void setAirplaneModeEnabled(boolean enabled) { + mIsAirplaneModeOn = enabled; + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java new file mode 100644 index 000000000000..5e1fea512d55 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java @@ -0,0 +1,385 @@ +package com.android.systemui.qs.tiles.dialog; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.telephony.TelephonyManager; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.recyclerview.widget.RecyclerView; +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.UiEventLogger; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; +import com.android.wifitrackerlib.WifiEntry; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.List; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class InternetDialogTest extends SysuiTestCase { + + private static final String MOBILE_NETWORK_TITLE = "Mobile Title"; + private static final String MOBILE_NETWORK_SUMMARY = "Mobile Summary"; + private static final String WIFI_TITLE = "Connected Wi-Fi Title"; + private static final String WIFI_SUMMARY = "Connected Wi-Fi Summary"; + + @Mock + private Handler mHandler; + @Mock + private TelephonyManager mTelephonyManager; + @Mock + private WifiManager mWifiManager; + @Mock + private WifiEntry mInternetWifiEntry; + @Mock + private List<WifiEntry> mWifiEntries; + @Mock + private InternetAdapter mInternetAdapter; + @Mock + private InternetDialogController mInternetDialogController; + + private FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock()); + private InternetDialog mInternetDialog; + private View mDialogView; + private View mSubTitle; + private LinearLayout mEthernet; + private LinearLayout mMobileDataToggle; + private LinearLayout mWifiToggle; + private LinearLayout mConnectedWifi; + private RecyclerView mWifiList; + private LinearLayout mSeeAll; + private LinearLayout mWifiScanNotify; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt()); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + when(mInternetWifiEntry.getTitle()).thenReturn(WIFI_TITLE); + when(mInternetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY); + when(mInternetWifiEntry.isDefaultNetwork()).thenReturn(true); + when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true); + when(mWifiEntries.size()).thenReturn(1); + + when(mInternetDialogController.getMobileNetworkTitle()).thenReturn(MOBILE_NETWORK_TITLE); + when(mInternetDialogController.getMobileNetworkSummary()) + .thenReturn(MOBILE_NETWORK_SUMMARY); + when(mInternetDialogController.getWifiManager()).thenReturn(mWifiManager); + + mInternetDialog = new InternetDialog(mContext, mock(InternetDialogFactory.class), + mInternetDialogController, true, true, true, mock(UiEventLogger.class), mHandler, + mBgExecutor); + mInternetDialog.mAdapter = mInternetAdapter; + mInternetDialog.onAccessPointsChanged(mWifiEntries, mInternetWifiEntry); + mInternetDialog.show(); + + mDialogView = mInternetDialog.mDialogView; + mSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle); + mEthernet = mDialogView.requireViewById(R.id.ethernet_layout); + mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_network_layout); + mWifiToggle = mDialogView.requireViewById(R.id.turn_on_wifi_layout); + mConnectedWifi = mDialogView.requireViewById(R.id.wifi_connected_layout); + mWifiList = mDialogView.requireViewById(R.id.wifi_list_layout); + mSeeAll = mDialogView.requireViewById(R.id.see_all_layout); + mWifiScanNotify = mDialogView.requireViewById(R.id.wifi_scan_notify_layout); + } + + @After + public void tearDown() { + mInternetDialog.dismissDialog(); + } + + @Test + public void hideWifiViews_WifiViewsGone() { + mInternetDialog.hideWifiViews(); + + assertThat(mInternetDialog.mIsProgressBarVisible).isFalse(); + assertThat(mWifiToggle.getVisibility()).isEqualTo(View.GONE); + assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE); + assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE); + assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateDialog_withApmOn_internetDialogSubTitleGone() { + when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); + + mInternetDialog.updateDialog(true); + + assertThat(mSubTitle.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateDialog_withApmOff_internetDialogSubTitleVisible() { + when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false); + + mInternetDialog.updateDialog(true); + + assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void updateDialog_apmOffAndHasEthernet_showEthernet() { + when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false); + when(mInternetDialogController.hasEthernet()).thenReturn(true); + + mInternetDialog.updateDialog(true); + + assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void updateDialog_apmOffAndNoEthernet_hideEthernet() { + when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false); + when(mInternetDialogController.hasEthernet()).thenReturn(false); + + mInternetDialog.updateDialog(true); + + assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateDialog_apmOnAndHasEthernet_showEthernet() { + when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); + when(mInternetDialogController.hasEthernet()).thenReturn(true); + + mInternetDialog.updateDialog(true); + + assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void updateDialog_apmOnAndNoEthernet_hideEthernet() { + when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); + when(mInternetDialogController.hasEthernet()).thenReturn(false); + + mInternetDialog.updateDialog(true); + + assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateDialog_withApmOn_mobileDataLayoutGone() { + when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); + + mInternetDialog.updateDialog(true); + + assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() { + // The preconditions WiFi ON and Internet WiFi are already in setUp() + doReturn(false).when(mInternetDialogController).activeNetworkIsCellular(); + + mInternetDialog.updateDialog(false); + + assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void updateDialog_wifiOnAndNoConnectedWifi_hideConnectedWifi() { + // The precondition WiFi ON is already in setUp() + mInternetDialog.onAccessPointsChanged(mWifiEntries, null /* connectedEntry*/); + doReturn(false).when(mInternetDialogController).activeNetworkIsCellular(); + + mInternetDialog.updateDialog(false); + + assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateDialog_wifiOnAndNoWifiList_hideWifiListAndSeeAll() { + // The precondition WiFi ON is already in setUp() + mInternetDialog.onAccessPointsChanged(null /* wifiEntries */, mInternetWifiEntry); + + mInternetDialog.updateDialog(false); + + assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE); + assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateDialog_wifiOnAndHasWifiList_showWifiListAndSeeAll() { + // The preconditions WiFi ON and WiFi entries are already in setUp() + + mInternetDialog.updateDialog(false); + + assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void updateDialog_deviceLockedAndHasInternetWifi_showHighlightWifiToggle() { + // The preconditions WiFi ON and Internet WiFi are already in setUp() + when(mInternetDialogController.isDeviceLocked()).thenReturn(true); + + mInternetDialog.updateDialog(false); + + assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mWifiToggle.getBackground()).isNotNull(); + } + + @Test + public void updateDialog_deviceLockedAndHasInternetWifi_hideConnectedWifi() { + // The preconditions WiFi ON and Internet WiFi are already in setUp() + when(mInternetDialogController.isDeviceLocked()).thenReturn(true); + + mInternetDialog.updateDialog(false); + + assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateDialog_deviceLockedAndHasWifiList_hideWifiListAndSeeAll() { + // The preconditions WiFi entries are already in setUp() + when(mInternetDialogController.isDeviceLocked()).thenReturn(true); + + mInternetDialog.updateDialog(false); + + assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE); + assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateDialog_wifiOn_hideWifiScanNotify() { + // The preconditions WiFi ON and Internet WiFi are already in setUp() + + mInternetDialog.updateDialog(false); + + assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateDialog_wifiOffAndWifiScanOff_hideWifiScanNotify() { + when(mWifiManager.isWifiEnabled()).thenReturn(false); + when(mInternetDialogController.isWifiScanEnabled()).thenReturn(false); + + mInternetDialog.updateDialog(false); + + assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateDialog_wifiOffAndWifiScanOnAndDeviceLocked_hideWifiScanNotify() { + when(mWifiManager.isWifiEnabled()).thenReturn(false); + when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true); + when(mInternetDialogController.isDeviceLocked()).thenReturn(true); + + mInternetDialog.updateDialog(false); + + assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateDialog_wifiOffAndWifiScanOnAndDeviceUnlocked_showWifiScanNotify() { + when(mWifiManager.isWifiEnabled()).thenReturn(false); + when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true); + when(mInternetDialogController.isDeviceLocked()).thenReturn(false); + + mInternetDialog.updateDialog(false); + + assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.VISIBLE); + TextView wifiScanNotifyText = mDialogView.requireViewById(R.id.wifi_scan_notify_text); + assertThat(wifiScanNotifyText.getText().length()).isNotEqualTo(0); + assertThat(wifiScanNotifyText.getMovementMethod()).isNotNull(); + } + + @Test + public void onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() { + mSeeAll.performClick(); + + verify(mInternetDialogController).launchNetworkSetting(); + } + + @Test + public void showProgressBar_wifiDisabled_hideProgressBar() { + Mockito.reset(mHandler); + when(mWifiManager.isWifiEnabled()).thenReturn(false); + + mInternetDialog.showProgressBar(); + + assertThat(mInternetDialog.mIsProgressBarVisible).isFalse(); + verify(mHandler, never()).postDelayed(any(Runnable.class), anyLong()); + } + + @Test + public void showProgressBar_deviceLocked_hideProgressBar() { + Mockito.reset(mHandler); + when(mInternetDialogController.isDeviceLocked()).thenReturn(true); + + mInternetDialog.showProgressBar(); + + assertThat(mInternetDialog.mIsProgressBarVisible).isFalse(); + verify(mHandler, never()).postDelayed(any(Runnable.class), anyLong()); + } + + @Test + public void showProgressBar_wifiEnabledWithWifiEntry_showProgressBarThenHide() { + Mockito.reset(mHandler); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + + mInternetDialog.showProgressBar(); + + // Show progress bar + assertThat(mInternetDialog.mIsProgressBarVisible).isTrue(); + + ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); + verify(mHandler).postDelayed(runnableCaptor.capture(), + eq(InternetDialog.PROGRESS_DELAY_MS)); + runnableCaptor.getValue().run(); + + // Then hide progress bar + assertThat(mInternetDialog.mIsProgressBarVisible).isFalse(); + } + + @Test + public void showProgressBar_wifiEnabledWithoutWifiEntries_showProgressBarThenHideSearch() { + Mockito.reset(mHandler); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + mInternetDialog.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry*/); + + mInternetDialog.showProgressBar(); + + // Show progress bar + assertThat(mInternetDialog.mIsProgressBarVisible).isTrue(); + + ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); + verify(mHandler).postDelayed(runnableCaptor.capture(), + eq(InternetDialog.PROGRESS_DELAY_MS)); + runnableCaptor.getValue().run(); + + // Then hide searching sub-title only + assertThat(mInternetDialog.mIsProgressBarVisible).isTrue(); + assertThat(mInternetDialog.mIsSearchingHidden).isTrue(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java index 10c878a92745..6f081c759df7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java @@ -34,6 +34,7 @@ import android.view.ScrollCaptureResponse; import androidx.test.filters.SmallTest; +import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.SysuiTestCase; import com.android.systemui.screenshot.ScrollCaptureClient.Session; @@ -274,7 +275,8 @@ public class ScrollCaptureControllerTest extends SysuiTestCase { when(client.start(/* response */ any(), /* maxPages */ anyFloat())) .thenReturn(immediateFuture(session)); return new ScrollCaptureController(context, context.getMainExecutor(), - client, new ImageTileSet(context.getMainThreadHandler())); + client, new ImageTileSet(context.getMainThreadHandler()), + new UiEventLoggerFake()); } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index 21c6292c151f..f3762c566731 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -423,17 +423,18 @@ public class CommandQueueTest extends SysuiTestCase { final boolean credentialAllowed = true; final boolean requireConfirmation = true; final int userId = 10; - final String packageName = "test"; final long operationId = 1; + final String packageName = "test"; + final long requestId = 10; final int multiSensorConfig = BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT; mCommandQueue.showAuthenticationDialog(promptInfo, receiver, sensorIds, - credentialAllowed, requireConfirmation , userId, packageName, operationId, + credentialAllowed, requireConfirmation, userId, operationId, packageName, requestId, multiSensorConfig); waitForIdleSync(); verify(mCallbacks).showAuthenticationDialog(eq(promptInfo), eq(receiver), eq(sensorIds), - eq(credentialAllowed), eq(requireConfirmation), eq(userId), eq(packageName), - eq(operationId), eq(multiSensorConfig)); + eq(credentialAllowed), eq(requireConfirmation), eq(userId), eq(operationId), + eq(packageName), eq(requestId), eq(multiSensorConfig)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index f5ce673c249e..f5cab1df9fa2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -23,6 +23,7 @@ import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST; @@ -111,6 +112,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { private static final ComponentName DEVICE_OWNER_COMPONENT = new ComponentName("com.android.foo", "bar"); + private String mKeyguardTryFingerprintMsg; private String mDisclosureWithOrganization; private String mDisclosureGeneric; private String mFinancedDisclosureWithOrganization; @@ -182,6 +184,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mContext.addMockSystemService(UserManager.class, mUserManager); mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class)); mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class)); + mKeyguardTryFingerprintMsg = mContext.getString(R.string.keyguard_try_fingerprint); mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name, ORGANIZATION_NAME); mDisclosureGeneric = mContext.getString(R.string.do_disclosure_generic); @@ -637,7 +640,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test - public void onRefreshBatteryInfo_fullChargedWithOverheat_presentCharged() { + public void onRefreshBatteryInfo_fullChargedWithOverheat_presentChargingLimited() { createController(); BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING, 100 /* level */, BatteryManager.BATTERY_PLUGGED_AC, @@ -649,6 +652,24 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { verifyIndicationMessage( INDICATION_TYPE_BATTERY, + mContext.getString( + R.string.keyguard_plugged_in_charging_limited, + NumberFormat.getPercentInstance().format(100 / 100f))); + } + + @Test + public void onRefreshBatteryInfo_fullChargedWithoutOverheat_presentCharged() { + createController(); + BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING, + 100 /* level */, BatteryManager.BATTERY_PLUGGED_AC, + BatteryManager.BATTERY_HEALTH_GOOD, 0 /* maxChargingWattage */, + true /* present */); + + mController.getKeyguardCallback().onRefreshBatteryInfo(status); + mController.setVisible(true); + + verifyIndicationMessage( + INDICATION_TYPE_BATTERY, mContext.getString(R.string.keyguard_charged)); } @@ -677,6 +698,48 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { verifyTransientMessage(message); } + @Test + public void faceAuthMessageSuppressed() { + createController(); + String faceHelpMsg = "Face auth help message"; + + // GIVEN state of showing message when keyguard screen is on + when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); + when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); + when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true); + + // GIVEN fingerprint is also running (not udfps) + when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); + when(mKeyguardUpdateMonitor.isUdfpsAvailable()).thenReturn(false); + + mController.setVisible(true); + + // WHEN a face help message comes in + mController.getKeyguardCallback().onBiometricHelp( + KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, faceHelpMsg, + BiometricSourceType.FACE); + + // THEN "try fingerprint" message appears (and not the face help message) + verifyTransientMessage(mKeyguardTryFingerprintMsg); + + // THEN the face help message is still announced for a11y + verify(mIndicationAreaBottom).announceForAccessibility(eq(faceHelpMsg)); + } + + @Test + public void testEmptyOwnerInfoHidesIndicationArea() { + createController(); + + // GIVEN the owner info is set to an empty string + when(mLockPatternUtils.getDeviceOwnerInfo()).thenReturn(""); + + // WHEN asked to update the indication area + mController.setVisible(true); + + // THEN the owner info should be hidden + verifyHideIndication(INDICATION_TYPE_OWNER_INFO); + } + private void sendUpdateDisclosureBroadcast() { mBroadcastReceiver.onReceive(mContext, new Intent()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt new file mode 100644 index 000000000000..97fe25d9a619 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 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.statusbar + +import android.testing.AndroidTestingRunner +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.util.function.Consumer + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class LightRevealScrimTest : SysuiTestCase() { + + private lateinit var scrim: LightRevealScrim + private var isOpaque = false + + @Before + fun setUp() { + scrim = LightRevealScrim(context, null) + scrim.isScrimOpaqueChangedListener = Consumer { opaque -> + isOpaque = opaque + } + scrim.revealAmount = 0f + assertTrue("Scrim is not opaque in initial setup", scrim.isScrimOpaque) + } + + @Test + fun testAlphaSetsOpaque() { + scrim.alpha = 0.5f + assertFalse("Scrim is opaque even though alpha is set", scrim.isScrimOpaque) + } + + @Test + fun testVisibilitySetsOpaque() { + scrim.visibility = View.INVISIBLE + assertFalse("Scrim is opaque even though it's invisible", scrim.isScrimOpaque) + scrim.visibility = View.GONE + assertFalse("Scrim is opaque even though it's gone", scrim.isScrimOpaque) + } + + @Test + fun testRevealSetsOpaque() { + scrim.revealAmount = 0.5f + assertFalse("Scrim is opaque even though it's revealed", scrim.isScrimOpaque) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt index a7b14460f925..465370b59553 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt @@ -25,6 +25,7 @@ import android.view.View import android.view.ViewRootImpl import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.Interpolators import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.phone.BiometricUnlockController @@ -32,6 +33,7 @@ import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.eq +import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Rule import org.junit.Test @@ -167,6 +169,22 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { } @Test + fun onPanelExpansionChanged_respectsMinPanelPullDownFraction() { + notificationShadeDepthController.panelPullDownMinFraction = 0.5f + notificationShadeDepthController.onPanelExpansionChanged(0.5f /* expansion */, + true /* tracking */) + assertThat(notificationShadeDepthController.shadeExpansion).isEqualTo(0f) + + notificationShadeDepthController.onPanelExpansionChanged(0.75f /* expansion */, + true /* tracking */) + assertThat(notificationShadeDepthController.shadeExpansion).isEqualTo(0.5f) + + notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */, + true /* tracking */) + assertThat(notificationShadeDepthController.shadeExpansion).isEqualTo(1f) + } + + @Test fun onStateChanged_reevalutesBlurs_ifSameRadiusAndNewState() { onPanelExpansionChanged_apliesBlur_ifShade() clearInvocations(choreographer) @@ -178,10 +196,21 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { @Test fun setQsPanelExpansion_appliesBlur() { + statusBarState = StatusBarState.KEYGUARD notificationShadeDepthController.qsPanelExpansion = 1f - notificationShadeDepthController.onPanelExpansionChanged(0.5f, tracking = false) + notificationShadeDepthController.onPanelExpansionChanged(1f, tracking = false) notificationShadeDepthController.updateBlurCallback.doFrame(0) - verify(blurUtils).applyBlur(any(), anyInt(), eq(false)) + verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false)) + } + + @Test + fun setQsPanelExpansion_easing() { + statusBarState = StatusBarState.KEYGUARD + notificationShadeDepthController.qsPanelExpansion = 0.25f + notificationShadeDepthController.onPanelExpansionChanged(1f, tracking = false) + notificationShadeDepthController.updateBlurCallback.doFrame(0) + verify(wallpaperManager).setWallpaperZoomOut(any(), + eq(Interpolators.getNotificationScrimAlpha(0.25f, false /* notifications */))) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt index 116f807a888d..9e103d68dd1e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt @@ -44,6 +44,8 @@ import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.FeatureFlags import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener +import com.android.systemui.statusbar.policy.DeviceProvisionedController +import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener import com.android.systemui.util.concurrency.FakeExecution import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any @@ -90,6 +92,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { @Mock private lateinit var statusBarStateController: StatusBarStateController @Mock + private lateinit var deviceProvisionedController: DeviceProvisionedController + @Mock private lateinit var handler: Handler @Mock @@ -107,12 +111,15 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { private lateinit var configChangeListenerCaptor: ArgumentCaptor<ConfigurationListener> @Captor private lateinit var statusBarStateListenerCaptor: ArgumentCaptor<StateListener> + @Captor + private lateinit var deviceProvisionedCaptor: ArgumentCaptor<DeviceProvisionedListener> private lateinit var sessionListener: OnTargetsAvailableListener private lateinit var userListener: UserTracker.Callback private lateinit var settingsObserver: ContentObserver private lateinit var configChangeListener: ConfigurationListener private lateinit var statusBarStateListener: StateListener + private lateinit var deviceProvisionedListener: DeviceProvisionedListener private val clock = FakeSystemClock() private val executor = FakeExecutor(clock) @@ -144,6 +151,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { `when`(plugin.getView(any())).thenReturn(fakeSmartspaceView) `when`(userTracker.userProfiles).thenReturn(userList) `when`(statusBarStateController.dozeAmount).thenReturn(0.5f) + `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true) + `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true) setActiveUser(userHandlePrimary) setAllowPrivateNotifications(userHandlePrimary, true) @@ -161,11 +170,15 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { contentResolver, configurationController, statusBarStateController, + deviceProvisionedController, execution, executor, handler, Optional.of(plugin) ) + + verify(deviceProvisionedController).addCallback(capture(deviceProvisionedCaptor)) + deviceProvisionedListener = deviceProvisionedCaptor.value } @Test(expected = RuntimeException::class) @@ -180,6 +193,27 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { } @Test + fun connectOnlyAfterDeviceIsProvisioned() { + // GIVEN an unprovisioned device and an attempt to connect + `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(false) + `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(false) + + // WHEN a connection attempt is made + controller.buildAndConnectView(fakeParent) + + // THEN no session is created + verify(smartspaceManager, never()).createSmartspaceSession(any()) + + // WHEN it does become provisioned + `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true) + `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true) + deviceProvisionedListener.onUserSetupChanged() + + // THEN the session is created + verify(smartspaceManager).createSmartspaceSession(any()) + } + + @Test fun testListenersAreRegistered() { // GIVEN a listener is added after a session is created connectSession() @@ -424,6 +458,20 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { assertEquals(fakeSmartspaceView, controller.view) } + @Test + fun testConnectAttemptBeforeInitializationShouldNotCreateSession() { + // GIVEN an uninitalized smartspaceView + // WHEN the device is provisioned + `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true) + `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true) + deviceProvisionedListener.onDeviceProvisionedChanged() + + // THEN no calls to createSmartspaceSession should occur + verify(smartspaceManager, never()).createSmartspaceSession(any()) + // THEN no listeners should be registered + verify(configurationController, never()).addCallback(any()) + } + private fun connectSession() { controller.buildAndConnectView(fakeParent) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index b03df880f0ba..4151ab2044f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -290,6 +290,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testUpdateFooter_noNotifications() { setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); + FooterView view = mock(FooterView.class); mStackScroller.setFooterView(view); mStackScroller.updateFooter(); @@ -299,6 +301,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testUpdateFooter_remoteInput() { setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); when(row.canViewBeDismissed()).thenReturn(true); @@ -318,6 +321,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testUpdateFooter_oneClearableNotification() { setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); when(mEmptyShadeView.getVisibility()).thenReturn(GONE); when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL)) @@ -331,8 +335,25 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test + public void testUpdateFooter_oneClearableNotification_beforeUserSetup() { + setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(false); + + when(mEmptyShadeView.getVisibility()).thenReturn(GONE); + when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL)) + .thenReturn(true); + when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true); + + FooterView view = mock(FooterView.class); + mStackScroller.setFooterView(view); + mStackScroller.updateFooter(); + verify(mStackScroller).updateFooterView(false, true, true); + } + + @Test public void testUpdateFooter_oneNonClearableNotification() { setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); when(row.canViewBeDismissed()).thenReturn(false); @@ -351,6 +372,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testUpdateFooter_atEnd() { + mStackScroller.setCurrentUserSetup(true); + // add footer mStackScroller.inflateFooterView(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java index f376e88b2cb1..42f38891b1bb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java @@ -74,6 +74,7 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; @@ -98,6 +99,7 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { @Mock private HeadsUpManagerPhone mHeadsUpManager; @Mock private NotificationRoundnessManager mNotificationRoundnessManager; @Mock private TunerService mTunerService; + @Mock private DeviceProvisionedController mDeviceProvisionedController; @Mock private DynamicPrivacyController mDynamicPrivacyController; @Mock private ConfigurationController mConfigurationController; @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; @@ -153,6 +155,7 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { mHeadsUpManager, mNotificationRoundnessManager, mTunerService, + mDeviceProvisionedController, mDynamicPrivacyController, mConfigurationController, mSysuiStatusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index 690b8415762d..1043faa8b1bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -16,24 +16,37 @@ package com.android.systemui.statusbar.phone; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + import static com.google.common.truth.Truth.assertThat; +import static org.mockito.AdditionalAnswers.returnsFirstArg; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.when; + +import android.content.res.Resources; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.doze.util.BurnInHelperKt; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; @SmallTest @RunWith(AndroidTestingRunner.class) public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private static final int SCREEN_HEIGHT = 2000; - private static final int EMPTY_MARGIN = 0; private static final int EMPTY_HEIGHT = 0; private static final float ZERO_DRAG = 0.f; private static final float OPAQUE = 1.f; @@ -41,10 +54,15 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private static final boolean HAS_CUSTOM_CLOCK = false; private static final boolean HAS_VISIBLE_NOTIFS = false; + @Mock + private Resources mResources; + private KeyguardClockPositionAlgorithm mClockPositionAlgorithm; private KeyguardClockPositionAlgorithm.Result mClockPosition; + private MockitoSession mStaticMockSession; private int mNotificationStackHeight; private float mPanelExpansion; + private int mKeyguardStatusBarHeaderHeight; private int mKeyguardStatusHeight; private float mDark; private boolean mHasCustomClock; @@ -52,16 +70,32 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private float mQsExpansion; private int mCutoutTopInset = 0; // in pixels private boolean mIsSplitShade = false; + private float mUdfpsTop = -1; + private float mClockBottom = SCREEN_HEIGHT / 2; + private boolean mClockTopAligned; @Before public void setUp() { + MockitoAnnotations.initMocks(this); + mStaticMockSession = mockitoSession() + .mockStatic(BurnInHelperKt.class) + .startMocking(); + mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm(); + when(mResources.getDimensionPixelSize(anyInt())).thenReturn(0); + mClockPositionAlgorithm.loadDimens(mResources); + mClockPosition = new KeyguardClockPositionAlgorithm.Result(); mHasCustomClock = HAS_CUSTOM_CLOCK; mHasVisibleNotifs = HAS_VISIBLE_NOTIFS; } + @After + public void tearDown() { + mStaticMockSession.finishMocking(); + } + @Test public void clockPositionTopOfScreenOnAOD() { // GIVEN on AOD and both stack scroll and clock have 0 height @@ -338,6 +372,155 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { assertThat(mClockPosition.clockAlpha).isEqualTo(TRANSPARENT); } + @Test + public void clockPositionMinimizesBurnInMovementToAvoidUdfpsOnAOD() { + // GIVEN a center aligned clock + mClockTopAligned = false; + + // GIVEN the clock + udfps are 100px apart + mClockBottom = SCREEN_HEIGHT - 500; + mUdfpsTop = SCREEN_HEIGHT - 400; + + // GIVEN it's AOD and the burn-in y value is 200 + givenAOD(); + givenMaxBurnInOffset(200); + + // WHEN the clock position algorithm is run with the highest burn in offset + givenHighestBurnInOffset(); + positionClock(); + + // THEN the worst-case clock Y position is shifted only by 100 (not the full 200), + // so that it's at the same location as mUdfpsTop + assertThat(mClockPosition.clockY).isEqualTo(100); + + // WHEN the clock position algorithm is run with the lowest burn in offset + givenLowestBurnInOffset(); + positionClock(); + + // THEN lowest case starts at mCutoutTopInset + assertThat(mClockPosition.clockY).isEqualTo(mCutoutTopInset); + } + + @Test + public void clockPositionShiftsToAvoidUdfpsOnAOD_usesSpaceAboveClock() { + // GIVEN a center aligned clock + mClockTopAligned = false; + + // GIVEN there's space at the top of the screen on LS (that's available to be used for + // burn-in on AOD) + mKeyguardStatusBarHeaderHeight = 150; + + // GIVEN the bottom of the clock is beyond the top of UDFPS + mClockBottom = SCREEN_HEIGHT - 300; + mUdfpsTop = SCREEN_HEIGHT - 400; + + // GIVEN it's AOD and the burn-in y value is 200 + givenAOD(); + givenMaxBurnInOffset(200); + + // WHEN the clock position algorithm is run with the highest burn in offset + givenHighestBurnInOffset(); + positionClock(); + + // THEN the algo should shift the clock up and use the area above the clock for + // burn-in since the burn in offset > space above clock + assertThat(mClockPosition.clockY).isEqualTo(mKeyguardStatusBarHeaderHeight); + + // WHEN the clock position algorithm is run with the lowest burn in offset + givenLowestBurnInOffset(); + positionClock(); + + // THEN lowest case starts at mCutoutTopInset (0 in this case) + assertThat(mClockPosition.clockY).isEqualTo(mCutoutTopInset); + } + + @Test + public void clockPositionShiftsToAvoidUdfpsOnAOD_usesMaxBurnInOffset() { + // GIVEN a center aligned clock + mClockTopAligned = false; + + // GIVEN there's 200px space at the top of the screen on LS (that's available to be used for + // burn-in on AOD) but 50px are taken up by the cutout + mKeyguardStatusBarHeaderHeight = 200; + mCutoutTopInset = 50; + + // GIVEN the bottom of the clock is beyond the top of UDFPS + mClockBottom = SCREEN_HEIGHT - 300; + mUdfpsTop = SCREEN_HEIGHT - 400; + + // GIVEN it's AOD and the burn-in y value is only 25px (less than space above clock) + givenAOD(); + int maxYBurnInOffset = 25; + givenMaxBurnInOffset(maxYBurnInOffset); + + // WHEN the clock position algorithm is run with the highest burn in offset + givenHighestBurnInOffset(); + positionClock(); + + // THEN the algo should shift the clock up and use the area above the clock for + // burn-in + assertThat(mClockPosition.clockY).isEqualTo(mKeyguardStatusBarHeaderHeight); + + // WHEN the clock position algorithm is run with the lowest burn in offset + givenLowestBurnInOffset(); + positionClock(); + + // THEN lowest case starts above mKeyguardStatusBarHeaderHeight + assertThat(mClockPosition.clockY).isEqualTo( + mKeyguardStatusBarHeaderHeight - 2 * maxYBurnInOffset); + } + + @Test + public void clockPositionShiftsToMaximizeUdfpsBurnInMovement() { + // GIVEN a center aligned clock + mClockTopAligned = false; + + // GIVEN there's 200px space at the top of the screen on LS (that's available to be used for + // burn-in on AOD) but 50px are taken up by the cutout + mKeyguardStatusBarHeaderHeight = 200; + mCutoutTopInset = 50; + int upperSpaceAvailable = mKeyguardStatusBarHeaderHeight - mCutoutTopInset; + + // GIVEN the bottom of the clock and the top of UDFPS are 100px apart + mClockBottom = SCREEN_HEIGHT - 500; + mUdfpsTop = SCREEN_HEIGHT - 400; + float lowerSpaceAvailable = mUdfpsTop - mClockBottom; + + // GIVEN it's AOD and the burn-in y value is 200 + givenAOD(); + givenMaxBurnInOffset(200); + + // WHEN the clock position algorithm is run with the highest burn in offset + givenHighestBurnInOffset(); + positionClock(); + + // THEN the algo should shift the clock up and use both the area above + // the clock and below the clock (vertically centered in its allowed area) + assertThat(mClockPosition.clockY).isEqualTo( + (int) (mCutoutTopInset + upperSpaceAvailable + lowerSpaceAvailable)); + + // WHEN the clock position algorithm is run with the lowest burn in offset + givenLowestBurnInOffset(); + positionClock(); + + // THEN lowest case starts at mCutoutTopInset + assertThat(mClockPosition.clockY).isEqualTo(mCutoutTopInset); + } + + private void givenHighestBurnInOffset() { + when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).then(returnsFirstArg()); + } + + private void givenLowestBurnInOffset() { + when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(0); + } + + private void givenMaxBurnInOffset(int offset) { + when(mResources.getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y_large_clock)) + .thenReturn(offset); + mClockPositionAlgorithm.loadDimens(mResources); + } + private void givenAOD() { mPanelExpansion = 1.f; mDark = 1.f; @@ -348,13 +531,33 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { mDark = 0.f; } + /** + * Setup and run the clock position algorithm. + * + * mClockPosition.clockY will contain the top y-coordinate for the clock position + */ private void positionClock() { - mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight, - mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, - 0 /* userSwitchHeight */, 0 /* userSwitchPreferredY */, - mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */, - 0 /* unlockedStackScrollerPadding */, mQsExpansion, - mCutoutTopInset, mIsSplitShade); + mClockPositionAlgorithm.setup( + mKeyguardStatusBarHeaderHeight, + SCREEN_HEIGHT, + mNotificationStackHeight, + mPanelExpansion, + SCREEN_HEIGHT, + mKeyguardStatusHeight, + 0 /* userSwitchHeight */, + 0 /* userSwitchPreferredY */, + mHasCustomClock, + mHasVisibleNotifs, + mDark, + ZERO_DRAG, + false /* bypassEnabled */, + 0 /* unlockedStackScrollerPadding */, + mQsExpansion, + mCutoutTopInset, + mIsSplitShade, + mUdfpsTop, + mClockBottom, + mClockTopAligned); mClockPositionAlgorithm.run(mClockPosition); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java index 525bb1c641fd..1b0b40408b60 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java @@ -142,7 +142,7 @@ import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class NotificationPanelViewTest extends SysuiTestCase { +public class NotificationPanelViewControllerTest extends SysuiTestCase { private static final int NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE = 50; @@ -474,6 +474,12 @@ public class NotificationPanelViewTest extends SysuiTestCase { } @Test + public void testSetMinFraction() { + mNotificationPanelViewController.setMinFraction(0.5f); + verify(mNotificationShadeDepthController).setPanelPullDownMinFraction(eq(0.5f)); + } + + @Test public void testSetDozing_notifiesNsslAndStateController() { mNotificationPanelViewController.setDozing(true /* dozing */, false /* animate */, null /* touch */); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java index ddd78541d113..90b8a74d88be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java @@ -146,7 +146,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { mNotificationShadeWindowController.attach(); clearInvocations(mWindowManager); - mNotificationShadeWindowController.setLightRevealScrimAmount(0f); + mNotificationShadeWindowController.setLightRevealScrimOpaque(true); verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); assertThat((mLayoutParameters.getValue().flags & FLAG_SHOW_WALLPAPER) == 0).isTrue(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java index 9b7c78f7dba1..aafaebd959f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java @@ -16,6 +16,11 @@ package com.android.systemui.statusbar.phone; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -56,6 +61,8 @@ import com.android.systemui.util.InjectionInflationController; 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; @@ -93,6 +100,10 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController; @Mock private LockIconViewController mLockIconViewController; + @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler> + mInteractionEventHandlerCaptor; + private NotificationShadeWindowView.InteractionEventHandler mInteractionEventHandler; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -150,4 +161,49 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { verify(mDragDownHelper).onTouchEvent(ev); ev.recycle(); } + + @Test + public void testInterceptTouchWhenShowingAltAuth() { + captureInteractionEventHandler(); + + // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept + when(mStatusBarStateController.isDozing()).thenReturn(false); + when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true); + when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false); + + // THEN we should intercept touch + assertTrue(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class))); + } + + @Test + public void testNoInterceptTouch() { + captureInteractionEventHandler(); + + // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept + when(mStatusBarStateController.isDozing()).thenReturn(false); + when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(false); + when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false); + + // THEN we shouldn't intercept touch + assertFalse(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class))); + } + + @Test + public void testHandleTouchEventWhenShowingAltAuth() { + captureInteractionEventHandler(); + + // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept + when(mStatusBarStateController.isDozing()).thenReturn(false); + when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true); + when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false); + + // THEN we should handle the touch + assertTrue(mInteractionEventHandler.handleTouchEvent(mock(MotionEvent.class))); + } + + private void captureInteractionEventHandler() { + verify(mView).setInteractionEventHandler(mInteractionEventHandlerCaptor.capture()); + mInteractionEventHandler = mInteractionEventHandlerCaptor.getValue(); + + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index c71318fa9464..47c8806be6d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -624,7 +624,7 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimTinted(Map.of( mScrimInFront, false, - mScrimBehind, false, + mScrimBehind, true, mScrimForBubble, true )); @@ -705,7 +705,7 @@ public class ScrimControllerTest extends SysuiTestCase { public void qsExpansion_half_clippingQs() { reset(mScrimBehind); mScrimController.setClipsQsScrim(true); - mScrimController.setQsPosition(0.5f, 999 /* value doesn't matter */); + mScrimController.setQsPosition(0.25f, 999 /* value doesn't matter */); finishAnimationsImmediately(); assertScrimAlpha(Map.of( @@ -744,13 +744,6 @@ public class ScrimControllerTest extends SysuiTestCase { finishAnimationsImmediately(); mScrimController.transitionTo(ScrimState.UNLOCKED); - // Immediately tinted black after the transition starts - assertScrimTinted(Map.of( - mScrimInFront, true, - mScrimBehind, true, - mScrimForBubble, true - )); - finishAnimationsImmediately(); // All scrims should be transparent at the end of fade transition. @@ -1143,7 +1136,7 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void testScrimsVisible_whenShadeVisibleOnLockscreen() { mScrimController.transitionTo(ScrimState.KEYGUARD); - mScrimController.setQsPosition(0.5f, 300); + mScrimController.setQsPosition(0.25f, 300); assertScrimAlpha(Map.of( mScrimBehind, SEMI_TRANSPARENT, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index c39a9061f95d..5ed69853e40f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -102,6 +102,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; @Mock + private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor; + @Mock private KeyguardMessageArea mKeyguardMessageArea; private WakefulnessLifecycle mWakefulnessLifecycle; @@ -287,6 +289,24 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + public void testShowing_whenAlternateAuthShowing() { + mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor); + when(mBouncer.isShowing()).thenReturn(false); + when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true); + assertTrue("Is showing not accurate when alternative auth showing", + mStatusBarKeyguardViewManager.isShowing()); + } + + @Test + public void testWillBeShowing_whenAlternateAuthShowing() { + mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor); + when(mBouncer.isShowing()).thenReturn(false); + when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true); + assertTrue("Is or will be showing not accurate when alternative auth showing", + mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()); + } + + @Test public void testUpdateResources_delegatesToBouncer() { mStatusBarKeyguardViewManager.updateResources(); @@ -299,18 +319,4 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { verify(mBouncer).updateKeyguardPosition(1.0f); } - - @Test - public void testNavBarHiddenWhenSleepAnimationStarts() { - mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */); - assertTrue(mStatusBarKeyguardViewManager.isNavBarVisible()); - - // Verify that the nav bar is hidden when the screen off animation starts - doReturn(true).when(mUnlockedScreenOffAnimationController).isScreenOffAnimationPlaying(); - mWakefulnessLifecycle.dispatchFinishedGoingToSleep(); - assertFalse(mStatusBarKeyguardViewManager.isNavBarVisible()); - - mWakefulnessLifecycle.dispatchFinishedWakingUp(); - assertTrue(mStatusBarKeyguardViewManager.isNavBarVisible()); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index bd9835c0e8e3..6051c5615f8d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -824,6 +824,34 @@ public class StatusBarTest extends SysuiTestCase { } @Test + public void testSetExpansionAffectsAlpha_onlyWhenHidingKeyguard() { + mStatusBar.updateScrimController(); + verify(mScrimController).setExpansionAffectsAlpha(eq(true)); + + clearInvocations(mScrimController); + when(mBiometricUnlockController.isBiometricUnlock()).thenReturn(true); + mStatusBar.updateScrimController(); + verify(mScrimController).setExpansionAffectsAlpha(eq(true)); + + clearInvocations(mScrimController); + when(mKeyguardStateController.isShowing()).thenReturn(true); + mStatusBar.updateScrimController(); + verify(mScrimController).setExpansionAffectsAlpha(eq(false)); + + clearInvocations(mScrimController); + reset(mKeyguardStateController); + when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true); + mStatusBar.updateScrimController(); + verify(mScrimController).setExpansionAffectsAlpha(eq(false)); + + clearInvocations(mScrimController); + reset(mKeyguardStateController); + when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true); + mStatusBar.updateScrimController(); + verify(mScrimController).setExpansionAffectsAlpha(eq(false)); + } + + @Test public void testTransitionLaunch_noPreview_doesntGoUnlocked() { mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); mStatusBar.showKeyguardImpl(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt index d26db4c69ece..b7c4d0a7d223 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt @@ -221,6 +221,18 @@ class OngoingCallControllerTest : SysuiTestCase() { verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean()) } + /** Regression test for b/201097913. */ + @Test + fun onEntryCleanUp_callNotifAddedThenRemoved_listenerNotified() { + val ongoingCallNotifEntry = createOngoingCallNotifEntry() + notifCollectionListener.onEntryAdded(ongoingCallNotifEntry) + reset(mockOngoingCallListener) + + notifCollectionListener.onEntryCleanUp(ongoingCallNotifEntry) + + verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean()) + } + /** Regression test for b/188491504. */ @Test fun onEntryRemoved_removedNotifHasSameKeyAsAddedNotif_listenerNotified() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java index 57198dbe18bb..4a5770d12239 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java @@ -238,7 +238,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { mNetworkController.setNoNetworksAvailable(false); setWifiStateForVcn(true, testSsid); setWifiLevelForVcn(0); - verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]); + verifyLastMobileDataIndicatorsForVcn(true, 0, TelephonyIcons.ICON_CWF, false); mNetworkController.setNoNetworksAvailable(true); for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) { @@ -246,11 +246,11 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { setConnectivityViaCallbackInNetworkControllerForVcn( NetworkCapabilities.TRANSPORT_CELLULAR, true, true, mVcnTransportInfo); - verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]); + verifyLastMobileDataIndicatorsForVcn(true, testLevel, TelephonyIcons.ICON_CWF, true); setConnectivityViaCallbackInNetworkControllerForVcn( NetworkCapabilities.TRANSPORT_CELLULAR, false, true, mVcnTransportInfo); - verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]); + verifyLastMobileDataIndicatorsForVcn(true, testLevel, TelephonyIcons.ICON_CWF, false); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt new file mode 100644 index 000000000000..871a48c503be --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2021 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.statusbar.policy + +import android.os.Handler +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +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.Mockito.`when` +import org.mockito.Mockito.anyString +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import java.util.Date + +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +@SmallTest +class VariableDateViewControllerTest : SysuiTestCase() { + + companion object { + private const val TIME_STAMP = 1_500_000_000_000 + private const val LONG_PATTERN = "EEEMMMd" + private const val SHORT_PATTERN = "MMMd" + private const val CHAR_WIDTH = 10f + } + + @Mock + private lateinit var broadcastDispatcher: BroadcastDispatcher + @Mock + private lateinit var view: VariableDateView + @Captor + private lateinit var onMeasureListenerCaptor: ArgumentCaptor<VariableDateView.OnMeasureListener> + + private var lastText: String? = null + + private lateinit var systemClock: FakeSystemClock + private lateinit var testableLooper: TestableLooper + private lateinit var testableHandler: Handler + private lateinit var controller: VariableDateViewController + + private lateinit var longText: String + private lateinit var shortText: String + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + testableLooper = TestableLooper.get(this) + testableHandler = Handler(testableLooper.looper) + + systemClock = FakeSystemClock() + systemClock.setCurrentTimeMillis(TIME_STAMP) + + `when`(view.longerPattern).thenReturn(LONG_PATTERN) + `when`(view.shorterPattern).thenReturn(SHORT_PATTERN) + `when`(view.handler).thenReturn(testableHandler) + + `when`(view.setText(anyString())).thenAnswer { + lastText = it.arguments[0] as? String + Unit + } + `when`(view.isAttachedToWindow).thenReturn(true) + + val date = Date(TIME_STAMP) + longText = getTextForFormat(date, getFormatFromPattern(LONG_PATTERN)) + shortText = getTextForFormat(date, getFormatFromPattern(SHORT_PATTERN)) + + // Assume some sizes for the text, the controller doesn't need to know if these sizes are + // the true ones + `when`(view.getDesiredWidthForText(any())).thenAnswer { + getTextLength(it.arguments[0] as CharSequence) + } + + controller = VariableDateViewController( + systemClock, + broadcastDispatcher, + testableHandler, + view + ) + + controller.init() + testableLooper.processAllMessages() + + verify(view).onAttach(capture(onMeasureListenerCaptor)) + } + + @Test + fun testViewStartsWithLongText() { + assertThat(lastText).isEqualTo(longText) + } + + @Test + fun testListenerNotNull() { + assertThat(onMeasureListenerCaptor.value).isNotNull() + } + + @Test + fun testLotsOfSpaceUseLongText() { + onMeasureListenerCaptor.value.onMeasureAction(10000) + + testableLooper.processAllMessages() + assertThat(lastText).isEqualTo(longText) + } + + @Test + fun testSmallSpaceUseEmpty() { + onMeasureListenerCaptor.value.onMeasureAction(1) + testableLooper.processAllMessages() + + assertThat(lastText).isEmpty() + } + + @Test + fun testSpaceInBetweenUseShortText() { + val average = ((getTextLength(longText) + getTextLength(shortText)) / 2).toInt() + + onMeasureListenerCaptor.value.onMeasureAction(average) + testableLooper.processAllMessages() + + assertThat(lastText).isEqualTo(shortText) + } + + @Test + fun testSwitchBackToLonger() { + onMeasureListenerCaptor.value.onMeasureAction(1) + testableLooper.processAllMessages() + + onMeasureListenerCaptor.value.onMeasureAction(10000) + testableLooper.processAllMessages() + + assertThat(lastText).isEqualTo(longText) + } + + @Test + fun testNoSwitchingWhenFrozen() { + `when`(view.freezeSwitching).thenReturn(true) + + val average = ((getTextLength(longText) + getTextLength(shortText)) / 2).toInt() + onMeasureListenerCaptor.value.onMeasureAction(average) + testableLooper.processAllMessages() + assertThat(lastText).isEqualTo(longText) + + onMeasureListenerCaptor.value.onMeasureAction(1) + testableLooper.processAllMessages() + assertThat(lastText).isEqualTo(longText) + } + + private fun getTextLength(text: CharSequence): Float { + return text.length * CHAR_WIDTH + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java index 9c47f19b20c8..2c461ae1b598 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java @@ -103,7 +103,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); - mManager = new ThemeOverlayApplier(mOverlayManager, MoreExecutors.directExecutor(), + mManager = new ThemeOverlayApplier(mOverlayManager, + MoreExecutors.directExecutor(), MoreExecutors.directExecutor(), LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager) { @Override protected OverlayManagerTransaction.Builder getTransactionBuilder() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt new file mode 100644 index 000000000000..eebcbe63d004 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2021 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.usb + +import android.app.PendingIntent +import android.content.Intent +import android.hardware.usb.IUsbSerialReader +import android.hardware.usb.UsbAccessory +import android.hardware.usb.UsbManager +import android.testing.AndroidTestingRunner +import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS +import androidx.test.filters.SmallTest +import androidx.test.rule.ActivityTestRule +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.lang.Exception + +/** + * UsbPermissionActivityTest + */ +@RunWith(AndroidTestingRunner::class) +@SmallTest +class UsbPermissionActivityTest : SysuiTestCase() { + + class UsbPermissionActivityTestable : UsbPermissionActivity() + + @Rule + @JvmField + var activityRule = ActivityTestRule<UsbPermissionActivityTestable>( + UsbPermissionActivityTestable::class.java, false, false) + + private val activityIntent = Intent(mContext, UsbPermissionActivityTestable::class.java) + .apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK + putExtra(UsbManager.EXTRA_PACKAGE, "com.android.systemui") + putExtra(Intent.EXTRA_INTENT, PendingIntent.getBroadcast( + mContext, + 334, + Intent("NO_ACTION"), + PendingIntent.FLAG_MUTABLE)) + putExtra(UsbManager.EXTRA_ACCESSORY, UsbAccessory( + "manufacturer", + "model", + "description", + "version", + "uri", + object : IUsbSerialReader.Stub() { + override fun getSerial(packageName: String): String { + return "serial" + } + })) + } + + @Before + fun setUp() { + activityRule.launchActivity(activityIntent) + } + + @After + fun tearDown() { + activityRule.finishActivity() + } + + @Test + @Throws(Exception::class) + fun testHideNonSystemOverlay() { + assertThat(activityRule.activity.window.attributes.privateFlags and + SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) + .isEqualTo(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java index a34c5986f36c..0e9d96c61e54 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java @@ -16,6 +16,8 @@ package com.android.systemui.util.sensors; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -338,30 +340,25 @@ public class ProximitySensorDualTest extends SysuiTestCase { @Test public void testSecondaryCancelsSecondary() { TestableListener listener = new TestableListener(); - ThresholdSensor.Listener cancelingListener = new ThresholdSensor.Listener() { - @Override - public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent event) { - mProximitySensor.pause(); - } - }; + ThresholdSensor.Listener cancelingListener = event -> mProximitySensor.pause(); mProximitySensor.register(listener); mProximitySensor.register(cancelingListener); - assertNull(listener.mLastEvent); - assertEquals(0, listener.mCallCount); + assertThat(listener.mLastEvent).isNull(); + assertThat(listener.mCallCount).isEqualTo(0); mThresholdSensorPrimary.triggerEvent(true, 0); - assertNull(listener.mLastEvent); - assertEquals(0, listener.mCallCount); + assertThat(listener.mLastEvent).isNull(); + assertThat(listener.mCallCount).isEqualTo(0); mThresholdSensorSecondary.triggerEvent(true, 0); - assertTrue(listener.mLastEvent.getBelow()); - assertEquals(1, listener.mCallCount); + assertThat(listener.mLastEvent.getBelow()).isTrue(); + assertThat(listener.mCallCount).isEqualTo(1); // The proximity sensor should now be canceled. Advancing the clock should do nothing. - assertEquals(0, mFakeExecutor.numPending()); + assertThat(mFakeExecutor.numPending()).isEqualTo(0); mThresholdSensorSecondary.triggerEvent(false, 1); - assertTrue(listener.mLastEvent.getBelow()); - assertEquals(1, listener.mCallCount); + assertThat(listener.mLastEvent.getBelow()).isTrue(); + assertThat(listener.mCallCount).isEqualTo(1); mProximitySensor.unregister(listener); } @@ -372,33 +369,66 @@ public class ProximitySensorDualTest extends SysuiTestCase { TestableListener listener = new TestableListener(); - // WE immediately register the secondary sensor. + // We immediately register the secondary sensor. mProximitySensor.register(listener); - assertFalse(mThresholdSensorPrimary.isPaused()); - assertFalse(mThresholdSensorSecondary.isPaused()); - assertNull(listener.mLastEvent); - assertEquals(0, listener.mCallCount); + assertThat(mThresholdSensorPrimary.isPaused()).isTrue(); + assertThat(mThresholdSensorSecondary.isPaused()).isFalse(); + assertThat(listener.mLastEvent).isNull(); + assertThat(listener.mCallCount).isEqualTo(0); mThresholdSensorPrimary.triggerEvent(true, 0); - assertNull(listener.mLastEvent); - assertEquals(0, listener.mCallCount); + assertThat(listener.mLastEvent).isNull(); + assertThat(listener.mCallCount).isEqualTo(0); mThresholdSensorSecondary.triggerEvent(true, 0); - assertTrue(listener.mLastEvent.getBelow()); - assertEquals(1, listener.mCallCount); + assertThat(listener.mLastEvent.getBelow()).isTrue(); + assertThat(listener.mCallCount).isEqualTo(1); // The secondary sensor should now remain resumed indefinitely. - assertFalse(mThresholdSensorSecondary.isPaused()); + assertThat(mThresholdSensorSecondary.isPaused()).isFalse(); mThresholdSensorSecondary.triggerEvent(false, 1); - assertFalse(listener.mLastEvent.getBelow()); - assertEquals(2, listener.mCallCount); + assertThat(listener.mLastEvent.getBelow()).isFalse(); + assertThat(listener.mCallCount).isEqualTo(2); // The secondary is still running, and not polling with the executor. - assertFalse(mThresholdSensorSecondary.isPaused()); - assertEquals(0, mFakeExecutor.numPending()); + assertThat(mThresholdSensorSecondary.isPaused()).isFalse(); + assertThat(mFakeExecutor.numPending()).isEqualTo(0); mProximitySensor.unregister(listener); } + @Test + public void testSecondaryPausesPrimary() { + TestableListener listener = new TestableListener(); + + mProximitySensor.register(listener); + + assertThat(mThresholdSensorPrimary.isPaused()).isFalse(); + assertThat(mThresholdSensorSecondary.isPaused()).isTrue(); + + mProximitySensor.setSecondarySafe(true); + + assertThat(mThresholdSensorPrimary.isPaused()).isTrue(); + assertThat(mThresholdSensorSecondary.isPaused()).isFalse(); + } + + @Test + public void testSecondaryResumesPrimary() { + mProximitySensor.setSecondarySafe(true); + + TestableListener listener = new TestableListener(); + mProximitySensor.register(listener); + + assertThat(mThresholdSensorPrimary.isPaused()).isTrue(); + assertThat(mThresholdSensorSecondary.isPaused()).isFalse(); + + mProximitySensor.setSecondarySafe(false); + + assertThat(mThresholdSensorPrimary.isPaused()).isFalse(); + assertThat(mThresholdSensorSecondary.isPaused()).isTrue(); + + + } + private static class TestableListener implements ThresholdSensor.Listener { ThresholdSensor.ThresholdSensorEvent mLastEvent; int mCallCount = 0; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java index 69764227040b..7bb26748a9d9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java @@ -31,6 +31,7 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti private final Map<SettingsKey, String> mValues = new HashMap<>(); private final Map<SettingsKey, List<ContentObserver>> mContentObservers = new HashMap<>(); + private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>(); public static final Uri CONTENT_URI = Uri.parse("content://settings/fake"); @@ -55,9 +56,15 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti @Override public void registerContentObserverForUser(Uri uri, boolean notifyDescendents, ContentObserver settingsObserver, int userHandle) { - SettingsKey key = new SettingsKey(userHandle, uri.toString()); - mContentObservers.putIfAbsent(key, new ArrayList<>()); - List<ContentObserver> observers = mContentObservers.get(key); + List<ContentObserver> observers; + if (userHandle == UserHandle.USER_ALL) { + mContentObserversAllUsers.putIfAbsent(uri.toString(), new ArrayList<>()); + observers = mContentObserversAllUsers.get(uri.toString()); + } else { + SettingsKey key = new SettingsKey(userHandle, uri.toString()); + mContentObservers.putIfAbsent(key, new ArrayList<>()); + observers = mContentObservers.get(key); + } observers.add(settingsObserver); } @@ -67,6 +74,10 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti List<ContentObserver> observers = mContentObservers.get(key); observers.remove(settingsObserver); } + for (String key : mContentObserversAllUsers.keySet()) { + List<ContentObserver> observers = mContentObserversAllUsers.get(key); + observers.remove(settingsObserver); + } } @Override @@ -114,6 +125,10 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti for (ContentObserver observer : mContentObservers.getOrDefault(key, new ArrayList<>())) { observer.dispatchChange(false, List.of(uri), userHandle); } + for (ContentObserver observer : + mContentObserversAllUsers.getOrDefault(uri.toString(), new ArrayList<>())) { + observer.dispatchChange(false, List.of(uri), userHandle); + } return true; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java index 0d560f237a07..34cae58d30e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.database.ContentObserver; +import android.os.UserHandle; import android.provider.Settings; import android.testing.AndroidTestingRunner; @@ -89,6 +90,16 @@ public class FakeSettingsTest extends SysuiTestCase { } @Test + public void testRegisterContentObserverAllUsers() { + mFakeSettings.registerContentObserverForUser( + mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL); + + mFakeSettings.putString("cat", "hat"); + + verify(mContentObserver).dispatchChange(anyBoolean(), any(Collection.class), anyInt()); + } + + @Test public void testUnregisterContentObserver() { mFakeSettings.registerContentObserver("cat", mContentObserver); mFakeSettings.unregisterContentObserver(mContentObserver); @@ -98,4 +109,16 @@ public class FakeSettingsTest extends SysuiTestCase { verify(mContentObserver, never()).dispatchChange( anyBoolean(), any(Collection.class), anyInt()); } + + @Test + public void testUnregisterContentObserverAllUsers() { + mFakeSettings.registerContentObserverForUser( + mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL); + mFakeSettings.unregisterContentObserver(mContentObserver); + + mFakeSettings.putString("cat", "hat"); + + verify(mContentObserver, never()).dispatchChange( + anyBoolean(), any(Collection.class), anyInt()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java index be110242a3eb..4f9cb35db1a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java @@ -51,6 +51,11 @@ public class FakeRotationLockController extends BaseLeakChecker<RotationLockCont } @Override + public boolean isCameraRotationEnabled() { + return false; + } + + @Override public void setRotationLockedAtAngle(boolean locked, int rotation) { } |