summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt75
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt174
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt255
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt65
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml13
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt111
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorKosmos.kt32
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt20
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelKosmos.kt8
17 files changed, 424 insertions, 461 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
index 4bc71fd6d363..75a77cf781d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
@@ -27,17 +27,6 @@ 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(
@@ -156,67 +145,3 @@ 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)
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt
deleted file mode 100644
index 298b54a5be5a..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2024 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.biometrics.domain.interactor
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-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.FingerprintSensorType
-import com.android.systemui.biometrics.shared.model.SensorStrength
-import com.android.systemui.biometrics.updateSfpsIndicatorRequests
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.display.data.repository.displayRepository
-import com.android.systemui.display.data.repository.displayStateRepository
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class SideFpsOverlayInteractorTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val underTest = kosmos.sideFpsOverlayInteractor
-
- @Test
- fun verifyIsShowingFalse_whenInRearDisplayMode() {
- kosmos.testScope.runTest {
- val isShowing by collectLastValue(underTest.isShowing)
- setupTestConfiguration(isInRearDisplayMode = true)
-
- updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
- runCurrent()
-
- assertThat(isShowing).isFalse()
- }
- }
-
- @Test
- fun verifyIsShowingUpdates_onPrimaryBouncerShowAndHide() {
- kosmos.testScope.runTest {
- val isShowing by collectLastValue(underTest.isShowing)
- setupTestConfiguration(isInRearDisplayMode = false)
-
- // Show primary bouncer
- updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
- runCurrent()
-
- assertThat(isShowing).isTrue()
-
- // Hide primary bouncer
- updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = false)
- runCurrent()
-
- assertThat(isShowing).isFalse()
- }
- }
-
- @Test
- fun verifyIsShowingUpdates_onAlternateBouncerShowAndHide() {
- kosmos.testScope.runTest {
- val isShowing by collectLastValue(underTest.isShowing)
- setupTestConfiguration(isInRearDisplayMode = false)
-
- updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = true)
- runCurrent()
-
- assertThat(isShowing).isTrue()
-
- // Hide alternate bouncer
- updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = false)
- runCurrent()
-
- assertThat(isShowing).isFalse()
- }
- }
-
- @Test
- fun verifyIsShowingUpdates_onSystemServerAuthenticationStartedAndStopped() {
- kosmos.testScope.runTest {
- val isShowing by collectLastValue(underTest.isShowing)
- setupTestConfiguration(isInRearDisplayMode = false)
-
- updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
- runCurrent()
-
- assertThat(isShowing).isTrue()
-
- // System server authentication stopped
- updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = false)
- runCurrent()
-
- assertThat(isShowing).isFalse()
- }
- }
-
- // 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 verifyIsShowingUpdates_onProgressBarInteraction() {
- kosmos.testScope.runTest {
- val isShowing by collectLastValue(underTest.isShowing)
- setupTestConfiguration(isInRearDisplayMode = false)
-
- updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
- runCurrent()
-
- assertThat(isShowing).isTrue()
-
- // updateSfpsIndicatorRequests(
- // kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
- // true
- // )
- runCurrent()
-
- assertThat(isShowing).isFalse()
-
- // Set progress bar invisible
- // updateSfpsIndicatorRequests(
- // kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
- // false
- // )
- runCurrent()
-
- // Verify indicator shown
- assertThat(isShowing).isTrue()
- }
- }
-
- private suspend fun TestScope.setupTestConfiguration(isInRearDisplayMode: Boolean) {
- kosmos.fingerprintPropertyRepository.setProperties(
- sensorId = 1,
- strength = SensorStrength.STRONG,
- sensorType = FingerprintSensorType.POWER_BUTTON,
- sensorLocations = emptyMap()
- )
-
- kosmos.displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode)
- kosmos.displayRepository.emitDisplayChangeEvent(0)
- runCurrent()
-
- kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
- AuthenticationReason.NotRunning
- )
- // TODO(b/365182034): set progress bar visibility once rest to unlock feature is implemented
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
index 2eea6681ecca..7fa165c19f60 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -16,48 +16,64 @@
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.biometrics.updateSfpsIndicatorRequests
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
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.firstValue
+import org.mockito.kotlin.argumentCaptor
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -67,25 +83,84 @@ 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
- @Captor private lateinit var viewCaptor: ArgumentCaptor<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,
+ }
@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(isInRearDisplayMode = true)
- updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
+ 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()
+
verify(kosmos.windowManager, never()).addView(any(), any())
}
}
@@ -93,14 +168,33 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() {
@Test
fun verifyIndicatorShowAndHide_onPrimaryBouncerShowAndHide() {
kosmos.testScope.runTest {
- setupTestConfiguration(isInRearDisplayMode = false)
- updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
+ 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
+ )
runCurrent()
verify(kosmos.windowManager).addView(any(), any())
// Hide primary bouncer
- updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = false)
+ updatePrimaryBouncer(
+ isShowing = false,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
runCurrent()
verify(kosmos.windowManager).removeView(any())
@@ -110,19 +204,30 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() {
@Test
fun verifyIndicatorShowAndHide_onAlternateBouncerShowAndHide() {
kosmos.testScope.runTest {
- setupTestConfiguration(isInRearDisplayMode = false)
- updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = true)
+ 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)
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)
)
- updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = false)
+ // Hide alternate bouncer
+ kosmos.keyguardBouncerRepository.setAlternateVisible(false)
runCurrent()
verify(kosmos.windowManager).removeView(any())
@@ -132,14 +237,30 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() {
@Test
fun verifyIndicatorShownAndHidden_onSystemServerAuthenticationStartedAndStopped() {
kosmos.testScope.runTest {
- setupTestConfiguration(isInRearDisplayMode = false)
- updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
+ 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
+ )
runCurrent()
verify(kosmos.windowManager).addView(any(), any())
// System server authentication stopped
- updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = false)
+ kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.NotRunning
+ )
runCurrent()
verify(kosmos.windowManager).removeView(any())
@@ -148,35 +269,45 @@ 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(isInRearDisplayMode = false)
- updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
+ 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
+ )
runCurrent()
val inOrder = inOrder(kosmos.windowManager)
+
// Verify indicator shown
inOrder.verify(kosmos.windowManager).addView(any(), any())
// Set progress bar visible
- // updateSfpsIndicatorRequests(
- // kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
- // true
- // )
+ kosmos.sideFpsProgressBarViewModel.setVisible(true)
+
runCurrent()
// Verify indicator hidden
inOrder.verify(kosmos.windowManager).removeView(any())
// Set progress bar invisible
- // updateSfpsIndicatorRequests(
- // kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
- // false
- // )
+ kosmos.sideFpsProgressBarViewModel.setVisible(false)
+
runCurrent()
// Verify indicator shown
@@ -184,18 +315,78 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() {
}
}
- private suspend fun TestScope.setupTestConfiguration(isInRearDisplayMode: Boolean) {
+ 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
+
kosmos.fingerprintPropertyRepository.setProperties(
sensorId = 1,
strength = SensorStrength.STRONG,
sensorType = FingerprintSensorType.POWER_BUTTON,
- sensorLocations = emptyMap()
+ sensorLocations = mapOf(DISPLAY_ID to sensorLocation)
)
kosmos.displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode)
- kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+ kosmos.displayStateRepository.setCurrentRotation(rotation)
kosmos.displayRepository.emitDisplayChangeEvent(0)
kosmos.sideFpsOverlayViewBinder.start()
runCurrent()
}
+
+ companion object {
+ private const val DISPLAY_ID = "displayId"
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
index 27b1371deb12..0db7b62b8ef1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -30,19 +30,23 @@ import android.view.windowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.airbnb.lottie.model.KeyPath
+import com.android.keyguard.keyguardUpdateMonitor
import com.android.settingslib.Utils
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.domain.interactor.displayStateInteractor
+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.LottieCallback
import com.android.systemui.biometrics.shared.model.SensorStrength
-import com.android.systemui.biometrics.updateSfpsIndicatorRequests
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.coroutines.collectLastValue
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
@@ -280,7 +284,17 @@ class SideFpsOverlayViewModelTest : SysuiTestCase() {
kosmos.testScope.runTest {
val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
- updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
+ kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.NotRunning
+ )
+ kosmos.sideFpsProgressBarViewModel.setVisible(false)
+
+ updatePrimaryBouncer(
+ isShowing = true,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
runCurrent()
assertThat(lottieCallbacks)
@@ -298,7 +312,17 @@ class SideFpsOverlayViewModelTest : SysuiTestCase() {
val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
setDarkMode(true)
- updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
+ kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.BiometricPromptAuthentication
+ )
+ kosmos.sideFpsProgressBarViewModel.setVisible(false)
+
+ updatePrimaryBouncer(
+ isShowing = false,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
runCurrent()
assertThat(lottieCallbacks)
@@ -314,7 +338,17 @@ class SideFpsOverlayViewModelTest : SysuiTestCase() {
val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
setDarkMode(false)
- updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
+ kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+ AuthenticationReason.BiometricPromptAuthentication
+ )
+ kosmos.sideFpsProgressBarViewModel.setVisible(false)
+
+ updatePrimaryBouncer(
+ isShowing = false,
+ isAnimatingAway = false,
+ fpsDetectionRunning = true,
+ isUnlockingWithFpAllowed = true
+ )
runCurrent()
assertThat(lottieCallbacks)
@@ -337,6 +371,29 @@ class SideFpsOverlayViewModelTest : SysuiTestCase() {
mContext.resources.configuration.uiMode = uiMode
}
+ 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,
diff --git a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
index 91cd019c85d1..3b3ed39c8993 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
@@ -215,4 +215,17 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0"
tools:srcCompat="@tools:sample/avatars" />
+
+ <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
+ android:id="@+id/biometric_icon_overlay"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_gravity="center"
+ android:contentDescription="@null"
+ android:scaleType="fitXY"
+ android:importantForAccessibility="no"
+ app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
+ app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
+ app:layout_constraintStart_toStartOf="@+id/biometric_icon"
+ app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
index 51117a7845df..2a00495e9d01 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
@@ -40,6 +40,19 @@ android:layout_height="match_parent">
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />
+ <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
+ android:id="@+id/biometric_icon_overlay"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_gravity="center"
+ android:contentDescription="@null"
+ android:scaleType="fitXY"
+ android:importantForAccessibility="no"
+ app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
+ app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
+ app:layout_constraintStart_toStartOf="@+id/biometric_icon"
+ app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
+
<ScrollView
android:id="@+id/scrollView"
android:layout_width="0dp"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
index 7ecbb88099cb..ec3fd9f7da35 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
@@ -25,8 +25,6 @@ import com.android.systemui.biometrics.domain.interactor.LogContextInteractor
import com.android.systemui.biometrics.domain.interactor.LogContextInteractorImpl
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
-import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractor
-import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractorImpl
import com.android.systemui.dagger.SysUISingleton
import dagger.Binds
import dagger.Module
@@ -48,12 +46,6 @@ interface BiometricsDomainLayerModule {
@Binds
@SysUISingleton
- fun providesSideFpsOverlayInteractor(
- impl: SideFpsOverlayInteractorImpl
- ): SideFpsOverlayInteractor
-
- @Binds
- @SysUISingleton
fun providesCredentialInteractor(impl: CredentialInteractorImpl): CredentialInteractor
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt
deleted file mode 100644
index 10c3483de452..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2024 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.biometrics.domain.interactor
-
-import android.util.Log
-import com.android.systemui.biometrics.shared.model.AuthenticationReason.NotRunning
-import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
-import com.android.systemui.util.kotlin.sample
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.onEach
-
-/** Encapsulates business logic for showing and hiding the side fingerprint sensor indicator. */
-interface SideFpsOverlayInteractor {
- /** Whether the side fingerprint sensor indicator is currently showing. */
- val isShowing: Flow<Boolean>
-}
-
-@OptIn(ExperimentalCoroutinesApi::class)
-class SideFpsOverlayInteractorImpl
-@Inject
-constructor(
- biometricStatusInteractor: BiometricStatusInteractor,
- displayStateInteractor: DisplayStateInteractor,
- deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor,
- sfpsSensorInteractor: SideFpsSensorInteractor,
- // TODO(b/365182034): add progress bar input when rest to unlock feature is implemented
-) : SideFpsOverlayInteractor {
- private val sfpsOverlayEnabled: Flow<Boolean> =
- sfpsSensorInteractor.isAvailable.sample(displayStateInteractor.isInRearDisplayMode) {
- isAvailable: Boolean,
- isInRearDisplayMode: Boolean ->
- isAvailable && !isInRearDisplayMode
- }
-
- private val showSideFpsOverlay: Flow<Boolean> =
- combine(
- biometricStatusInteractor.sfpsAuthenticationReason,
- deviceEntrySideFpsOverlayInteractor.showIndicatorForDeviceEntry,
- // TODO(b/365182034): add progress bar input when rest to unlock feature is implemented
- ) { systemServerAuthReason, showIndicatorForDeviceEntry ->
- Log.d(
- TAG,
- "systemServerAuthReason = $systemServerAuthReason, " +
- "showIndicatorForDeviceEntry = $showIndicatorForDeviceEntry, "
- )
- systemServerAuthReason != NotRunning || showIndicatorForDeviceEntry
- }
-
- override val isShowing: Flow<Boolean> =
- sfpsOverlayEnabled
- .flatMapLatest { sfpsOverlayEnabled ->
- if (!sfpsOverlayEnabled) {
- flowOf(false)
- } else {
- showSideFpsOverlay
- }
- }
- .onEach { Log.d(TAG, "isShowing: $it") }
-
- companion object {
- private const val TAG = "SideFpsOverlayInteractor"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index d055731b2698..73f75a4ff639 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -18,11 +18,13 @@ package com.android.systemui.biometrics.ui.binder
import android.animation.Animator
import android.animation.AnimatorSet
+import android.animation.ValueAnimator
import android.graphics.Outline
import android.graphics.Rect
import android.transition.AutoTransition
import android.transition.TransitionManager
import android.util.TypedValue
+import android.view.Surface
import android.view.View
import android.view.ViewGroup
import android.view.ViewOutlineProvider
@@ -158,13 +160,16 @@ object BiometricViewSizeBinder {
fun setVisibilities(hideSensorIcon: Boolean, size: PromptSize) {
viewsToHideWhenSmall.forEach { it.showContentOrHide(forceHide = size.isSmall) }
largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+ largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
largeConstraintSet.setVisibility(R.id.scrollView, View.GONE)
if (hideSensorIcon) {
smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+ smallConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
smallConstraintSet.setVisibility(R.id.indicator, View.GONE)
mediumConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+ mediumConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
mediumConstraintSet.setVisibility(R.id.indicator, View.GONE)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
index 9fe1dc51f4c2..9578da4238ee 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -33,44 +33,89 @@ import com.airbnb.lottie.LottieProperty
import com.android.app.animation.Interpolators
import com.android.keyguard.KeyguardPINView
import com.android.systemui.CoreStartable
-import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractor
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
+import com.android.systemui.biometrics.shared.model.AuthenticationReason.NotRunning
import com.android.systemui.biometrics.shared.model.LottieCallback
import com.android.systemui.biometrics.ui.viewmodel.SideFpsOverlayViewModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
+import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.sample
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
/** Binds the side fingerprint sensor indicator view to [SideFpsOverlayViewModel]. */
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class SideFpsOverlayViewBinder
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
@Application private val applicationContext: Context,
+ private val biometricStatusInteractor: Lazy<BiometricStatusInteractor>,
+ private val displayStateInteractor: Lazy<DisplayStateInteractor>,
+ private val deviceEntrySideFpsOverlayInteractor: Lazy<DeviceEntrySideFpsOverlayInteractor>,
private val layoutInflater: Lazy<LayoutInflater>,
- private val sideFpsOverlayInteractor: Lazy<SideFpsOverlayInteractor>,
- private val sideFpsOverlayViewModel: Lazy<SideFpsOverlayViewModel>,
+ private val sideFpsProgressBarViewModel: Lazy<SideFpsProgressBarViewModel>,
+ private val sfpsSensorInteractor: Lazy<SideFpsSensorInteractor>,
private val windowManager: Lazy<WindowManager>
) : CoreStartable {
- private var overlayView: View? = null
override fun start() {
- applicationScope.launch {
- sideFpsOverlayInteractor.get().isShowing.collect { isShowing: Boolean ->
- if (isShowing) {
- show()
- } else {
- hide()
+ applicationScope
+ .launch {
+ sfpsSensorInteractor.get().isAvailable.collect { isSfpsAvailable ->
+ if (isSfpsAvailable) {
+ combine(
+ biometricStatusInteractor.get().sfpsAuthenticationReason,
+ deviceEntrySideFpsOverlayInteractor
+ .get()
+ .showIndicatorForDeviceEntry,
+ sideFpsProgressBarViewModel.get().isVisible,
+ ::Triple
+ )
+ .sample(displayStateInteractor.get().isInRearDisplayMode, ::Pair)
+ .collect { (combinedFlows, isInRearDisplayMode: Boolean) ->
+ val (
+ systemServerAuthReason,
+ showIndicatorForDeviceEntry,
+ progressBarIsVisible) =
+ combinedFlows
+ Log.d(
+ TAG,
+ "systemServerAuthReason = $systemServerAuthReason, " +
+ "showIndicatorForDeviceEntry = " +
+ "$showIndicatorForDeviceEntry, " +
+ "progressBarIsVisible = $progressBarIsVisible"
+ )
+ if (!isInRearDisplayMode) {
+ if (progressBarIsVisible) {
+ hide()
+ } else if (systemServerAuthReason != NotRunning) {
+ show()
+ } else if (showIndicatorForDeviceEntry) {
+ show()
+ } else {
+ hide()
+ }
+ }
+ }
+ }
}
}
- }
}
+ private var overlayView: View? = null
+
/** Show the side fingerprint sensor indicator */
private fun show() {
if (overlayView?.isAttachedToWindow == true) {
@@ -80,10 +125,17 @@ constructor(
)
return
}
+
overlayView = layoutInflater.get().inflate(R.layout.sidefps_view, null, false)
- val overlayViewModel = sideFpsOverlayViewModel.get()
- bind(overlayView!!, overlayViewModel, windowManager.get())
+ val overlayViewModel =
+ SideFpsOverlayViewModel(
+ applicationContext,
+ deviceEntrySideFpsOverlayInteractor.get(),
+ displayStateInteractor.get(),
+ sfpsSensorInteractor.get(),
+ )
+ bind(overlayView!!, overlayViewModel, windowManager.get())
overlayView!!.visibility = View.INVISIBLE
Log.d(TAG, "show(): adding overlayView $overlayView")
windowManager.get().addView(overlayView, overlayViewModel.defaultOverlayViewParams)
@@ -109,20 +161,6 @@ constructor(
companion object {
private const val TAG = "SideFpsOverlayViewBinder"
- private val accessibilityDelegate =
- object : View.AccessibilityDelegate() {
- override fun dispatchPopulateAccessibilityEvent(
- host: View,
- event: AccessibilityEvent
- ): Boolean {
- return if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
- true
- } else {
- super.dispatchPopulateAccessibilityEvent(host, event)
- }
- }
- }
-
/** Binds overlayView (side fingerprint sensor indicator view) to SideFpsOverlayViewModel */
fun bind(
overlayView: View,
@@ -146,7 +184,24 @@ constructor(
overlayShowAnimator.start()
- it.accessibilityDelegate = accessibilityDelegate
+ it.setAccessibilityDelegate(
+ object : View.AccessibilityDelegate() {
+ override fun dispatchPopulateAccessibilityEvent(
+ host: View,
+ event: AccessibilityEvent
+ ): Boolean {
+ return if (
+ event.getEventType() ===
+ android.view.accessibility.AccessibilityEvent
+ .TYPE_WINDOW_STATE_CHANGED
+ ) {
+ true
+ } else {
+ super.dispatchPopulateAccessibilityEvent(host, event)
+ }
+ }
+ }
+ )
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 85f221fa951e..168ba11309cc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -78,11 +78,11 @@ import kotlinx.coroutines.launch
class PromptViewModel
@Inject
constructor(
- private val displayStateInteractor: DisplayStateInteractor,
+ displayStateInteractor: DisplayStateInteractor,
private val promptSelectorInteractor: PromptSelectorInteractor,
@Application private val context: Context,
- udfpsOverlayInteractor: UdfpsOverlayInteractor,
- biometricStatusInteractor: BiometricStatusInteractor,
+ private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
+ private val biometricStatusInteractor: BiometricStatusInteractor,
private val udfpsUtils: UdfpsUtils,
private val iconProvider: IconProvider,
private val activityTaskManager: ActivityTaskManager,
@@ -135,13 +135,11 @@ constructor(
R.dimen.biometric_prompt_landscape_medium_horizontal_padding
)
- val currentRotation: StateFlow<DisplayRotation> = displayStateInteractor.currentRotation
-
val udfpsOverlayParams: StateFlow<UdfpsOverlayParams> =
udfpsOverlayInteractor.udfpsOverlayParams
private val udfpsSensorBounds: Flow<Rect> =
- combine(udfpsOverlayParams, currentRotation) { params, rotation ->
+ combine(udfpsOverlayParams, displayStateInteractor.currentRotation) { params, rotation ->
val rotatedBounds = Rect(params.sensorBounds)
RotationUtils.rotateBounds(
rotatedBounds,
@@ -264,7 +262,7 @@ constructor(
_forceLargeSize,
promptKind,
displayStateInteractor.isLargeScreen,
- currentRotation,
+ displayStateInteractor.currentRotation,
modalities
) { forceLarge, promptKind, isLargeScreen, rotation, modalities ->
when {
@@ -456,7 +454,7 @@ constructor(
/** Padding for prompt UI elements */
val promptPadding: Flow<Rect> =
- combine(size, currentRotation) { size, rotation ->
+ combine(size, displayStateInteractor.currentRotation) { size, rotation ->
if (size != PromptSize.LARGE) {
val navBarInsets = Utils.getNavbarInsets(context)
if (rotation == DisplayRotation.ROTATION_90) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
index 7c1984e506c9..c2a4ee36dec6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
@@ -147,7 +147,8 @@ constructor(
_lottieBounds,
sensorLocation,
displayRotation,
- ) { _: Rect?, sensorLocation: SideFpsSensorLocation, _: DisplayRotation ->
+ ) { bounds: Rect?, sensorLocation: SideFpsSensorLocation, displayRotation: DisplayRotation
+ ->
val topLeft = Point(sensorLocation.left, sensorLocation.top)
defaultOverlayViewParams.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
index 75e38714f1fa..c5909ed24c50 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
@@ -107,8 +107,6 @@ constructor(
}
}
- // TODO(b/365182034): move to interactor, add as dependency of SideFpsOverlayInteractor when
- // rest to unlock feature is implemented
val isVisible: Flow<Boolean> = _visible.asStateFlow()
val progress: Flow<Float> = _progress.asStateFlow()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 48505102eba5..55fd3440ea07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -54,7 +54,6 @@ import com.android.systemui.biometrics.data.repository.biometricStatusRepository
import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor
-import com.android.systemui.biometrics.domain.interactor.sideFpsOverlayInteractor
import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
import com.android.systemui.biometrics.extractAuthenticatorTypes
import com.android.systemui.biometrics.faceSensorPropertiesInternal
@@ -1454,15 +1453,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun switch_to_credential_fallback() = runGenericTest {
val size by collectLastValue(kosmos.promptViewModel.size)
- val isShowingSfpsIndicator by collectLastValue(kosmos.sideFpsOverlayInteractor.isShowing)
// TODO(b/251476085): remove Spaghetti, migrate logic, and update this test
kosmos.promptViewModel.onSwitchToCredential()
assertThat(size).isEqualTo(PromptSize.LARGE)
- if (testCase.modalities.hasSfps) {
- assertThat(isShowingSfpsIndicator).isFalse()
- }
}
@Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorKosmos.kt
deleted file mode 100644
index 15c7e25f8a5c..000000000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorKosmos.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2024 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.biometrics.domain.interactor
-
-import com.android.systemui.keyguard.domain.interactor.deviceEntrySideFpsOverlayInteractor
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-@OptIn(ExperimentalCoroutinesApi::class)
-val Kosmos.sideFpsOverlayInteractor by Fixture {
- SideFpsOverlayInteractorImpl(
- biometricStatusInteractor,
- displayStateInteractor,
- deviceEntrySideFpsOverlayInteractor,
- sideFpsSensorInteractor,
- )
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt
index 59809e3d253f..79d58a1d4e40 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt
@@ -19,19 +19,27 @@ package com.android.systemui.biometrics.ui.binder
import android.content.applicationContext
import android.view.layoutInflater
import android.view.windowManager
-import com.android.systemui.biometrics.domain.interactor.sideFpsOverlayInteractor
-import com.android.systemui.biometrics.ui.viewmodel.sideFpsOverlayViewModel
+import com.android.systemui.biometrics.domain.interactor.biometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.sideFpsSensorInteractor
+import com.android.systemui.keyguard.domain.interactor.deviceEntrySideFpsOverlayInteractor
+import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+@OptIn(ExperimentalCoroutinesApi::class)
val Kosmos.sideFpsOverlayViewBinder by Fixture {
SideFpsOverlayViewBinder(
- applicationCoroutineScope,
- applicationContext,
+ applicationScope = applicationCoroutineScope,
+ applicationContext = applicationContext,
+ { biometricStatusInteractor },
+ { displayStateInteractor },
+ { deviceEntrySideFpsOverlayInteractor },
{ layoutInflater },
- { sideFpsOverlayInteractor },
- { sideFpsOverlayViewModel },
+ { sideFpsProgressBarViewModel },
+ { sideFpsSensorInteractor },
{ windowManager }
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelKosmos.kt
index e10b2dd6497d..de038559fc38 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelKosmos.kt
@@ -27,9 +27,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
@OptIn(ExperimentalCoroutinesApi::class)
val Kosmos.sideFpsOverlayViewModel by Fixture {
SideFpsOverlayViewModel(
- applicationContext,
- deviceEntrySideFpsOverlayInteractor,
- displayStateInteractor,
- sideFpsSensorInteractor,
+ applicationContext = applicationContext,
+ deviceEntrySideFpsOverlayInteractor = deviceEntrySideFpsOverlayInteractor,
+ displayStateInteractor = displayStateInteractor,
+ sfpsSensorInteractor = sideFpsSensorInteractor,
)
}