Loading packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt +84 −9 Original line number Diff line number Diff line Loading @@ -27,12 +27,23 @@ import android.hardware.face.FaceSensorProperties import android.hardware.face.FaceSensorPropertiesInternal import android.hardware.fingerprint.FingerprintSensorProperties import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.SysuiTestableContext import com.android.systemui.biometrics.data.repository.biometricStatusRepository import com.android.systemui.biometrics.shared.model.AuthenticationReason import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.res.R import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent /** Create [FingerprintSensorPropertiesInternal] for a test. */ internal fun fingerprintSensorPropertiesInternal( ids: List<Int> = listOf(0), strong: Boolean = true, sensorType: Int = FingerprintSensorProperties.TYPE_REAR sensorType: Int = FingerprintSensorProperties.TYPE_REAR, ): List<FingerprintSensorPropertiesInternal> { val componentInfo = listOf( Loading @@ -41,15 +52,15 @@ internal fun fingerprintSensorPropertiesInternal( "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */, "00000001" /* serialNumber */, "" /* softwareVersion */ "", /* softwareVersion */ ), ComponentInfoInternal( "matchingAlgorithm" /* componentId */, "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */, "vendor/version/revision" /* softwareVersion */ ) "vendor/version/revision", /* softwareVersion */ ), ) return ids.map { id -> FingerprintSensorPropertiesInternal( Loading @@ -58,7 +69,7 @@ internal fun fingerprintSensorPropertiesInternal( 5 /* maxEnrollmentsPerUser */, componentInfo, sensorType, false /* resetLockoutRequiresHardwareAuthToken */ false, /* resetLockoutRequiresHardwareAuthToken */ ) } } Loading @@ -75,15 +86,15 @@ internal fun faceSensorPropertiesInternal( "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */, "00000001" /* serialNumber */, "" /* softwareVersion */ "", /* softwareVersion */ ), ComponentInfoInternal( "matchingAlgorithm" /* componentId */, "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */, "vendor/version/revision" /* softwareVersion */ ) "vendor/version/revision", /* softwareVersion */ ), ) return ids.map { id -> FaceSensorPropertiesInternal( Loading @@ -94,7 +105,7 @@ internal fun faceSensorPropertiesInternal( FaceSensorProperties.TYPE_RGB, true /* supportsFaceDetection */, true /* supportsSelfIllumination */, false /* resetLockoutRequiresHardwareAuthToken */ false, /* resetLockoutRequiresHardwareAuthToken */ ) } } Loading Loading @@ -145,3 +156,67 @@ internal fun promptInfo( info.negativeButtonText = negativeButton return info } @OptIn(ExperimentalCoroutinesApi::class) internal fun TestScope.updateSfpsIndicatorRequests( kosmos: Kosmos, mContext: SysuiTestableContext, primaryBouncerRequest: Boolean? = null, alternateBouncerRequest: Boolean? = null, biometricPromptRequest: Boolean? = null, // TODO(b/365182034): update when rest to unlock feature is implemented // progressBarShowing: Boolean? = null ) { biometricPromptRequest?.let { hasBiometricPromptRequest -> if (hasBiometricPromptRequest) { kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.BiometricPromptAuthentication ) } else { kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.NotRunning ) } } primaryBouncerRequest?.let { hasPrimaryBouncerRequest -> updatePrimaryBouncer( kosmos, mContext, isShowing = hasPrimaryBouncerRequest, isAnimatingAway = false, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true, ) } alternateBouncerRequest?.let { hasAlternateBouncerRequest -> kosmos.keyguardBouncerRepository.setAlternateVisible(hasAlternateBouncerRequest) } // TODO(b/365182034): set progress bar visibility when rest to unlock feature is implemented runCurrent() } internal fun updatePrimaryBouncer( kosmos: Kosmos, mContext: SysuiTestableContext, isShowing: Boolean, isAnimatingAway: Boolean, fpsDetectionRunning: Boolean, isUnlockingWithFpAllowed: Boolean, ) { kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing) kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false) val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation( primaryStartDisappearAnimation ) whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning) .thenReturn(fpsDetectionRunning) whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) .thenReturn(isUnlockingWithFpAllowed) mContext.orCreateTestableResources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, true) } packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt +34 −223 Original line number Diff line number Diff line Loading @@ -16,64 +16,48 @@ package com.android.systemui.biometrics.ui.binder import android.animation.Animator import android.graphics.Rect import android.hardware.biometrics.SensorLocationInternal import android.hardware.display.DisplayManager import android.hardware.display.DisplayManagerGlobal import android.testing.TestableLooper import android.view.Display import android.view.DisplayInfo import android.view.LayoutInflater import android.view.View import android.view.ViewPropertyAnimator import android.view.WindowInsets import android.view.WindowManager import android.view.WindowMetrics import android.view.layoutInflater import android.view.windowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.airbnb.lottie.LottieAnimationView import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider import com.android.systemui.biometrics.data.repository.biometricStatusRepository import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.AuthenticationReason import com.android.systemui.biometrics.shared.model.DisplayRotation import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository import com.android.systemui.biometrics.updateSfpsIndicatorRequests import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.display.data.repository.displayStateRepository import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.testKosmos import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Ignore import org.junit.Rule 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 import org.mockito.Mockito.any import org.mockito.Mockito.inOrder import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.firstValue @OptIn(ExperimentalCoroutinesApi::class) @SmallTest Loading @@ -83,84 +67,25 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() { private val kosmos = testKosmos() @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() @Mock private lateinit var displayManager: DisplayManager @Mock private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider @Mock private lateinit var layoutInflater: LayoutInflater @Mock private lateinit var sideFpsView: View private val contextDisplayInfo = DisplayInfo() private var displayWidth: Int = 0 private var displayHeight: Int = 0 private var boundsWidth: Int = 0 private var boundsHeight: Int = 0 private lateinit var deviceConfig: DeviceConfig private lateinit var sensorLocation: SensorLocationInternal enum class DeviceConfig { X_ALIGNED, Y_ALIGNED, } @Captor private lateinit var viewCaptor: ArgumentCaptor<View> @Before fun setup() { allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread mContext = spy(mContext) val resources = mContext.resources whenever(mContext.display) .thenReturn( Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources) ) kosmos.layoutInflater = layoutInflater whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser) .thenReturn(MutableStateFlow(false)) context.addMockSystemService(DisplayManager::class.java, displayManager) context.addMockSystemService(WindowManager::class.java, kosmos.windowManager) `when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sideFpsView) `when`(sideFpsView.requireViewById<LottieAnimationView>(eq(R.id.sidefps_animation))) .thenReturn(mock(LottieAnimationView::class.java)) with(mock(ViewPropertyAnimator::class.java)) { `when`(sideFpsView.animate()).thenReturn(this) `when`(alpha(Mockito.anyFloat())).thenReturn(this) `when`(setStartDelay(Mockito.anyLong())).thenReturn(this) `when`(setDuration(Mockito.anyLong())).thenReturn(this) `when`(setListener(any())).thenAnswer { (it.arguments[0] as Animator.AnimatorListener).onAnimationEnd( mock(Animator::class.java) ) this } } } @Test fun verifyIndicatorNotAdded_whenInRearDisplayMode() { kosmos.testScope.runTest { setupTestConfiguration( DeviceConfig.X_ALIGNED, rotation = DisplayRotation.ROTATION_0, isInRearDisplayMode = true ) kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.NotRunning ) kosmos.sideFpsProgressBarViewModel.setVisible(false) updatePrimaryBouncer( isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true ) runCurrent() setupTestConfiguration(isInRearDisplayMode = true) updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true) verify(kosmos.windowManager, never()).addView(any(), any()) } } Loading @@ -168,33 +93,14 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() { @Test fun verifyIndicatorShowAndHide_onPrimaryBouncerShowAndHide() { kosmos.testScope.runTest { setupTestConfiguration( DeviceConfig.X_ALIGNED, rotation = DisplayRotation.ROTATION_0, isInRearDisplayMode = false ) kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.NotRunning ) kosmos.sideFpsProgressBarViewModel.setVisible(false) // Show primary bouncer updatePrimaryBouncer( isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true ) setupTestConfiguration(isInRearDisplayMode = false) updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true) runCurrent() verify(kosmos.windowManager).addView(any(), any()) // Hide primary bouncer updatePrimaryBouncer( isShowing = false, isAnimatingAway = false, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true ) updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = false) runCurrent() verify(kosmos.windowManager).removeView(any()) Loading @@ -204,30 +110,19 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() { @Test fun verifyIndicatorShowAndHide_onAlternateBouncerShowAndHide() { kosmos.testScope.runTest { setupTestConfiguration( DeviceConfig.X_ALIGNED, rotation = DisplayRotation.ROTATION_0, isInRearDisplayMode = false ) kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.NotRunning ) kosmos.sideFpsProgressBarViewModel.setVisible(false) // Show alternate bouncer kosmos.keyguardBouncerRepository.setAlternateVisible(true) setupTestConfiguration(isInRearDisplayMode = false) updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = true) runCurrent() verify(kosmos.windowManager).addView(any(), any()) var viewCaptor = argumentCaptor<View>() verify(kosmos.windowManager).addView(viewCaptor.capture(), any()) verify(viewCaptor.firstValue) .announceForAccessibility( mContext.getText(R.string.accessibility_side_fingerprint_indicator_label) ) // Hide alternate bouncer kosmos.keyguardBouncerRepository.setAlternateVisible(false) updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = false) runCurrent() verify(kosmos.windowManager).removeView(any()) Loading @@ -237,30 +132,14 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() { @Test fun verifyIndicatorShownAndHidden_onSystemServerAuthenticationStartedAndStopped() { kosmos.testScope.runTest { setupTestConfiguration( DeviceConfig.X_ALIGNED, rotation = DisplayRotation.ROTATION_0, isInRearDisplayMode = false ) kosmos.sideFpsProgressBarViewModel.setVisible(false) updatePrimaryBouncer( isShowing = false, isAnimatingAway = false, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true ) // System server authentication started kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.BiometricPromptAuthentication ) setupTestConfiguration(isInRearDisplayMode = false) updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true) runCurrent() verify(kosmos.windowManager).addView(any(), any()) // System server authentication stopped kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.NotRunning ) updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = false) runCurrent() verify(kosmos.windowManager).removeView(any()) Loading @@ -269,45 +148,37 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() { // On progress bar shown - hide indicator // On progress bar hidden - show indicator // TODO(b/365182034): update + enable when rest to unlock feature is implemented @Ignore("b/365182034") @Test fun verifyIndicatorProgressBarInteraction() { kosmos.testScope.runTest { // Pre-auth conditions setupTestConfiguration( DeviceConfig.X_ALIGNED, rotation = DisplayRotation.ROTATION_0, isInRearDisplayMode = false ) kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.NotRunning ) kosmos.sideFpsProgressBarViewModel.setVisible(false) // Show primary bouncer updatePrimaryBouncer( isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true ) setupTestConfiguration(isInRearDisplayMode = false) updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true) runCurrent() val inOrder = inOrder(kosmos.windowManager) // Verify indicator shown inOrder.verify(kosmos.windowManager).addView(any(), any()) // Set progress bar visible kosmos.sideFpsProgressBarViewModel.setVisible(true) updateSfpsIndicatorRequests( kosmos, mContext, primaryBouncerRequest = true, ) // , progressBarShowing = true) runCurrent() // Verify indicator hidden inOrder.verify(kosmos.windowManager).removeView(any()) // Set progress bar invisible kosmos.sideFpsProgressBarViewModel.setVisible(false) updateSfpsIndicatorRequests( kosmos, mContext, primaryBouncerRequest = true, ) // , progressBarShowing = false) runCurrent() // Verify indicator shown Loading @@ -315,78 +186,18 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() { } } private fun updatePrimaryBouncer( isShowing: Boolean, isAnimatingAway: Boolean, fpsDetectionRunning: Boolean, isUnlockingWithFpAllowed: Boolean, ) { kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing) kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false) val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation( primaryStartDisappearAnimation ) whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning) .thenReturn(fpsDetectionRunning) whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) .thenReturn(isUnlockingWithFpAllowed) mContext.orCreateTestableResources.addOverride( R.bool.config_show_sidefps_hint_on_bouncer, true ) } private suspend fun TestScope.setupTestConfiguration( deviceConfig: DeviceConfig, rotation: DisplayRotation = DisplayRotation.ROTATION_0, isInRearDisplayMode: Boolean, ) { this@SideFpsOverlayViewBinderTest.deviceConfig = deviceConfig when (deviceConfig) { DeviceConfig.X_ALIGNED -> { displayWidth = 3000 displayHeight = 1500 boundsWidth = 200 boundsHeight = 100 sensorLocation = SensorLocationInternal("", 2500, 0, boundsWidth / 2) } DeviceConfig.Y_ALIGNED -> { displayWidth = 2500 displayHeight = 2000 boundsWidth = 100 boundsHeight = 200 sensorLocation = SensorLocationInternal("", displayWidth, 300, boundsHeight / 2) } } whenever(kosmos.windowManager.maximumWindowMetrics) .thenReturn( WindowMetrics( Rect(0, 0, displayWidth, displayHeight), mock(WindowInsets::class.java), ) ) contextDisplayInfo.uniqueId = DISPLAY_ID private suspend fun TestScope.setupTestConfiguration(isInRearDisplayMode: Boolean) { kosmos.fingerprintPropertyRepository.setProperties( sensorId = 1, strength = SensorStrength.STRONG, sensorType = FingerprintSensorType.POWER_BUTTON, sensorLocations = mapOf(DISPLAY_ID to sensorLocation) sensorLocations = emptyMap(), ) kosmos.displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode) kosmos.displayStateRepository.setCurrentRotation(rotation) kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0) kosmos.displayRepository.emitDisplayChangeEvent(0) kosmos.sideFpsOverlayViewBinder.start() runCurrent() } companion object { private const val DISPLAY_ID = "displayId" } } packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt +12 −69 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt +84 −9 Original line number Diff line number Diff line Loading @@ -27,12 +27,23 @@ import android.hardware.face.FaceSensorProperties import android.hardware.face.FaceSensorPropertiesInternal import android.hardware.fingerprint.FingerprintSensorProperties import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.SysuiTestableContext import com.android.systemui.biometrics.data.repository.biometricStatusRepository import com.android.systemui.biometrics.shared.model.AuthenticationReason import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.res.R import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent /** Create [FingerprintSensorPropertiesInternal] for a test. */ internal fun fingerprintSensorPropertiesInternal( ids: List<Int> = listOf(0), strong: Boolean = true, sensorType: Int = FingerprintSensorProperties.TYPE_REAR sensorType: Int = FingerprintSensorProperties.TYPE_REAR, ): List<FingerprintSensorPropertiesInternal> { val componentInfo = listOf( Loading @@ -41,15 +52,15 @@ internal fun fingerprintSensorPropertiesInternal( "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */, "00000001" /* serialNumber */, "" /* softwareVersion */ "", /* softwareVersion */ ), ComponentInfoInternal( "matchingAlgorithm" /* componentId */, "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */, "vendor/version/revision" /* softwareVersion */ ) "vendor/version/revision", /* softwareVersion */ ), ) return ids.map { id -> FingerprintSensorPropertiesInternal( Loading @@ -58,7 +69,7 @@ internal fun fingerprintSensorPropertiesInternal( 5 /* maxEnrollmentsPerUser */, componentInfo, sensorType, false /* resetLockoutRequiresHardwareAuthToken */ false, /* resetLockoutRequiresHardwareAuthToken */ ) } } Loading @@ -75,15 +86,15 @@ internal fun faceSensorPropertiesInternal( "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */, "00000001" /* serialNumber */, "" /* softwareVersion */ "", /* softwareVersion */ ), ComponentInfoInternal( "matchingAlgorithm" /* componentId */, "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */, "vendor/version/revision" /* softwareVersion */ ) "vendor/version/revision", /* softwareVersion */ ), ) return ids.map { id -> FaceSensorPropertiesInternal( Loading @@ -94,7 +105,7 @@ internal fun faceSensorPropertiesInternal( FaceSensorProperties.TYPE_RGB, true /* supportsFaceDetection */, true /* supportsSelfIllumination */, false /* resetLockoutRequiresHardwareAuthToken */ false, /* resetLockoutRequiresHardwareAuthToken */ ) } } Loading Loading @@ -145,3 +156,67 @@ internal fun promptInfo( info.negativeButtonText = negativeButton return info } @OptIn(ExperimentalCoroutinesApi::class) internal fun TestScope.updateSfpsIndicatorRequests( kosmos: Kosmos, mContext: SysuiTestableContext, primaryBouncerRequest: Boolean? = null, alternateBouncerRequest: Boolean? = null, biometricPromptRequest: Boolean? = null, // TODO(b/365182034): update when rest to unlock feature is implemented // progressBarShowing: Boolean? = null ) { biometricPromptRequest?.let { hasBiometricPromptRequest -> if (hasBiometricPromptRequest) { kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.BiometricPromptAuthentication ) } else { kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.NotRunning ) } } primaryBouncerRequest?.let { hasPrimaryBouncerRequest -> updatePrimaryBouncer( kosmos, mContext, isShowing = hasPrimaryBouncerRequest, isAnimatingAway = false, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true, ) } alternateBouncerRequest?.let { hasAlternateBouncerRequest -> kosmos.keyguardBouncerRepository.setAlternateVisible(hasAlternateBouncerRequest) } // TODO(b/365182034): set progress bar visibility when rest to unlock feature is implemented runCurrent() } internal fun updatePrimaryBouncer( kosmos: Kosmos, mContext: SysuiTestableContext, isShowing: Boolean, isAnimatingAway: Boolean, fpsDetectionRunning: Boolean, isUnlockingWithFpAllowed: Boolean, ) { kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing) kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false) val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation( primaryStartDisappearAnimation ) whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning) .thenReturn(fpsDetectionRunning) whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) .thenReturn(isUnlockingWithFpAllowed) mContext.orCreateTestableResources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, true) }
packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt +34 −223 Original line number Diff line number Diff line Loading @@ -16,64 +16,48 @@ package com.android.systemui.biometrics.ui.binder import android.animation.Animator import android.graphics.Rect import android.hardware.biometrics.SensorLocationInternal import android.hardware.display.DisplayManager import android.hardware.display.DisplayManagerGlobal import android.testing.TestableLooper import android.view.Display import android.view.DisplayInfo import android.view.LayoutInflater import android.view.View import android.view.ViewPropertyAnimator import android.view.WindowInsets import android.view.WindowManager import android.view.WindowMetrics import android.view.layoutInflater import android.view.windowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.airbnb.lottie.LottieAnimationView import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider import com.android.systemui.biometrics.data.repository.biometricStatusRepository import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.AuthenticationReason import com.android.systemui.biometrics.shared.model.DisplayRotation import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository import com.android.systemui.biometrics.updateSfpsIndicatorRequests import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.display.data.repository.displayStateRepository import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.testKosmos import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Ignore import org.junit.Rule 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 import org.mockito.Mockito.any import org.mockito.Mockito.inOrder import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.firstValue @OptIn(ExperimentalCoroutinesApi::class) @SmallTest Loading @@ -83,84 +67,25 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() { private val kosmos = testKosmos() @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() @Mock private lateinit var displayManager: DisplayManager @Mock private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider @Mock private lateinit var layoutInflater: LayoutInflater @Mock private lateinit var sideFpsView: View private val contextDisplayInfo = DisplayInfo() private var displayWidth: Int = 0 private var displayHeight: Int = 0 private var boundsWidth: Int = 0 private var boundsHeight: Int = 0 private lateinit var deviceConfig: DeviceConfig private lateinit var sensorLocation: SensorLocationInternal enum class DeviceConfig { X_ALIGNED, Y_ALIGNED, } @Captor private lateinit var viewCaptor: ArgumentCaptor<View> @Before fun setup() { allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread mContext = spy(mContext) val resources = mContext.resources whenever(mContext.display) .thenReturn( Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources) ) kosmos.layoutInflater = layoutInflater whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser) .thenReturn(MutableStateFlow(false)) context.addMockSystemService(DisplayManager::class.java, displayManager) context.addMockSystemService(WindowManager::class.java, kosmos.windowManager) `when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sideFpsView) `when`(sideFpsView.requireViewById<LottieAnimationView>(eq(R.id.sidefps_animation))) .thenReturn(mock(LottieAnimationView::class.java)) with(mock(ViewPropertyAnimator::class.java)) { `when`(sideFpsView.animate()).thenReturn(this) `when`(alpha(Mockito.anyFloat())).thenReturn(this) `when`(setStartDelay(Mockito.anyLong())).thenReturn(this) `when`(setDuration(Mockito.anyLong())).thenReturn(this) `when`(setListener(any())).thenAnswer { (it.arguments[0] as Animator.AnimatorListener).onAnimationEnd( mock(Animator::class.java) ) this } } } @Test fun verifyIndicatorNotAdded_whenInRearDisplayMode() { kosmos.testScope.runTest { setupTestConfiguration( DeviceConfig.X_ALIGNED, rotation = DisplayRotation.ROTATION_0, isInRearDisplayMode = true ) kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.NotRunning ) kosmos.sideFpsProgressBarViewModel.setVisible(false) updatePrimaryBouncer( isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true ) runCurrent() setupTestConfiguration(isInRearDisplayMode = true) updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true) verify(kosmos.windowManager, never()).addView(any(), any()) } } Loading @@ -168,33 +93,14 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() { @Test fun verifyIndicatorShowAndHide_onPrimaryBouncerShowAndHide() { kosmos.testScope.runTest { setupTestConfiguration( DeviceConfig.X_ALIGNED, rotation = DisplayRotation.ROTATION_0, isInRearDisplayMode = false ) kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.NotRunning ) kosmos.sideFpsProgressBarViewModel.setVisible(false) // Show primary bouncer updatePrimaryBouncer( isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true ) setupTestConfiguration(isInRearDisplayMode = false) updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true) runCurrent() verify(kosmos.windowManager).addView(any(), any()) // Hide primary bouncer updatePrimaryBouncer( isShowing = false, isAnimatingAway = false, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true ) updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = false) runCurrent() verify(kosmos.windowManager).removeView(any()) Loading @@ -204,30 +110,19 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() { @Test fun verifyIndicatorShowAndHide_onAlternateBouncerShowAndHide() { kosmos.testScope.runTest { setupTestConfiguration( DeviceConfig.X_ALIGNED, rotation = DisplayRotation.ROTATION_0, isInRearDisplayMode = false ) kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.NotRunning ) kosmos.sideFpsProgressBarViewModel.setVisible(false) // Show alternate bouncer kosmos.keyguardBouncerRepository.setAlternateVisible(true) setupTestConfiguration(isInRearDisplayMode = false) updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = true) runCurrent() verify(kosmos.windowManager).addView(any(), any()) var viewCaptor = argumentCaptor<View>() verify(kosmos.windowManager).addView(viewCaptor.capture(), any()) verify(viewCaptor.firstValue) .announceForAccessibility( mContext.getText(R.string.accessibility_side_fingerprint_indicator_label) ) // Hide alternate bouncer kosmos.keyguardBouncerRepository.setAlternateVisible(false) updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = false) runCurrent() verify(kosmos.windowManager).removeView(any()) Loading @@ -237,30 +132,14 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() { @Test fun verifyIndicatorShownAndHidden_onSystemServerAuthenticationStartedAndStopped() { kosmos.testScope.runTest { setupTestConfiguration( DeviceConfig.X_ALIGNED, rotation = DisplayRotation.ROTATION_0, isInRearDisplayMode = false ) kosmos.sideFpsProgressBarViewModel.setVisible(false) updatePrimaryBouncer( isShowing = false, isAnimatingAway = false, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true ) // System server authentication started kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.BiometricPromptAuthentication ) setupTestConfiguration(isInRearDisplayMode = false) updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true) runCurrent() verify(kosmos.windowManager).addView(any(), any()) // System server authentication stopped kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.NotRunning ) updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = false) runCurrent() verify(kosmos.windowManager).removeView(any()) Loading @@ -269,45 +148,37 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() { // On progress bar shown - hide indicator // On progress bar hidden - show indicator // TODO(b/365182034): update + enable when rest to unlock feature is implemented @Ignore("b/365182034") @Test fun verifyIndicatorProgressBarInteraction() { kosmos.testScope.runTest { // Pre-auth conditions setupTestConfiguration( DeviceConfig.X_ALIGNED, rotation = DisplayRotation.ROTATION_0, isInRearDisplayMode = false ) kosmos.biometricStatusRepository.setFingerprintAuthenticationReason( AuthenticationReason.NotRunning ) kosmos.sideFpsProgressBarViewModel.setVisible(false) // Show primary bouncer updatePrimaryBouncer( isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, isUnlockingWithFpAllowed = true ) setupTestConfiguration(isInRearDisplayMode = false) updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true) runCurrent() val inOrder = inOrder(kosmos.windowManager) // Verify indicator shown inOrder.verify(kosmos.windowManager).addView(any(), any()) // Set progress bar visible kosmos.sideFpsProgressBarViewModel.setVisible(true) updateSfpsIndicatorRequests( kosmos, mContext, primaryBouncerRequest = true, ) // , progressBarShowing = true) runCurrent() // Verify indicator hidden inOrder.verify(kosmos.windowManager).removeView(any()) // Set progress bar invisible kosmos.sideFpsProgressBarViewModel.setVisible(false) updateSfpsIndicatorRequests( kosmos, mContext, primaryBouncerRequest = true, ) // , progressBarShowing = false) runCurrent() // Verify indicator shown Loading @@ -315,78 +186,18 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() { } } private fun updatePrimaryBouncer( isShowing: Boolean, isAnimatingAway: Boolean, fpsDetectionRunning: Boolean, isUnlockingWithFpAllowed: Boolean, ) { kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing) kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false) val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation( primaryStartDisappearAnimation ) whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning) .thenReturn(fpsDetectionRunning) whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) .thenReturn(isUnlockingWithFpAllowed) mContext.orCreateTestableResources.addOverride( R.bool.config_show_sidefps_hint_on_bouncer, true ) } private suspend fun TestScope.setupTestConfiguration( deviceConfig: DeviceConfig, rotation: DisplayRotation = DisplayRotation.ROTATION_0, isInRearDisplayMode: Boolean, ) { this@SideFpsOverlayViewBinderTest.deviceConfig = deviceConfig when (deviceConfig) { DeviceConfig.X_ALIGNED -> { displayWidth = 3000 displayHeight = 1500 boundsWidth = 200 boundsHeight = 100 sensorLocation = SensorLocationInternal("", 2500, 0, boundsWidth / 2) } DeviceConfig.Y_ALIGNED -> { displayWidth = 2500 displayHeight = 2000 boundsWidth = 100 boundsHeight = 200 sensorLocation = SensorLocationInternal("", displayWidth, 300, boundsHeight / 2) } } whenever(kosmos.windowManager.maximumWindowMetrics) .thenReturn( WindowMetrics( Rect(0, 0, displayWidth, displayHeight), mock(WindowInsets::class.java), ) ) contextDisplayInfo.uniqueId = DISPLAY_ID private suspend fun TestScope.setupTestConfiguration(isInRearDisplayMode: Boolean) { kosmos.fingerprintPropertyRepository.setProperties( sensorId = 1, strength = SensorStrength.STRONG, sensorType = FingerprintSensorType.POWER_BUTTON, sensorLocations = mapOf(DISPLAY_ID to sensorLocation) sensorLocations = emptyMap(), ) kosmos.displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode) kosmos.displayStateRepository.setCurrentRotation(rotation) kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0) kosmos.displayRepository.emitDisplayChangeEvent(0) kosmos.sideFpsOverlayViewBinder.start() runCurrent() } companion object { private const val DISPLAY_ID = "displayId" } }
packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt +12 −69 File changed.Preview size limit exceeded, changes collapsed. Show changes