diff options
28 files changed, 465 insertions, 346 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt index 97ac8c62d69d..d3049d9080f3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt @@ -6,6 +6,7 @@ import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.scene.SceneTestUtils import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController @@ -38,6 +39,7 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { private val testUtils = SceneTestUtils(this) private val testScope = testUtils.testScope private val userRepository = FakeUserRepository() + private val keyguardRepository = FakeKeyguardRepository() private lateinit var underTest: DeviceEntryRepository @@ -55,6 +57,7 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { lockPatternUtils = lockPatternUtils, keyguardBypassController = keyguardBypassController, keyguardStateController = keyguardStateController, + keyguardRepository = keyguardRepository, ) testScope.runCurrent() } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt index cd764c0cc1a4..b91541813d62 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt @@ -1,6 +1,5 @@ package com.android.systemui.deviceentry -import com.android.systemui.deviceentry.data.repository.DeviceEntryHapticsRepositoryModule import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import dagger.Module @@ -10,7 +9,6 @@ import dagger.multibindings.Multibinds includes = [ DeviceEntryRepositoryModule::class, - DeviceEntryHapticsRepositoryModule::class, ], ) abstract class DeviceEntryModule { diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt deleted file mode 100644 index 1458404446e6..000000000000 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2023 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.deviceentry.data.repository - -import com.android.systemui.dagger.SysUISingleton -import dagger.Binds -import dagger.Module -import javax.inject.Inject -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow - -/** Interface for classes that can access device-entry haptics application state. */ -interface DeviceEntryHapticsRepository { - /** - * Whether a successful biometric haptic has been requested. Has not yet been handled if true. - */ - val successHapticRequest: Flow<Boolean> - - /** Whether an error biometric haptic has been requested. Has not yet been handled if true. */ - val errorHapticRequest: Flow<Boolean> - - fun requestSuccessHaptic() - fun handleSuccessHaptic() - fun requestErrorHaptic() - fun handleErrorHaptic() -} - -/** Encapsulates application state for device entry haptics. */ -@SysUISingleton -class DeviceEntryHapticsRepositoryImpl @Inject constructor() : DeviceEntryHapticsRepository { - private val _successHapticRequest = MutableStateFlow(false) - override val successHapticRequest: Flow<Boolean> = _successHapticRequest.asStateFlow() - - private val _errorHapticRequest = MutableStateFlow(false) - override val errorHapticRequest: Flow<Boolean> = _errorHapticRequest.asStateFlow() - - override fun requestSuccessHaptic() { - _successHapticRequest.value = true - } - - override fun handleSuccessHaptic() { - _successHapticRequest.value = false - } - - override fun requestErrorHaptic() { - _errorHapticRequest.value = true - } - - override fun handleErrorHaptic() { - _errorHapticRequest.value = false - } -} - -@Module -interface DeviceEntryHapticsRepositoryModule { - @Binds fun repository(impl: DeviceEntryHapticsRepositoryImpl): DeviceEntryHapticsRepository -} diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt index f27bbe6c7624..08e8c2d8271f 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt @@ -7,26 +7,36 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.keyguard.shared.model.BiometricUnlockModel +import com.android.systemui.keyguard.shared.model.BiometricUnlockSource import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.user.data.repository.UserRepository +import com.android.systemui.util.kotlin.sample import dagger.Binds import dagger.Module import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext /** Interface for classes that can access device-entry-related application state. */ interface DeviceEntryRepository { + /** Whether the device is immediately entering the device after a biometric unlock. */ + val enteringDeviceFromBiometricUnlock: Flow<BiometricUnlockSource> + /** * Whether the device is unlocked. * @@ -73,7 +83,14 @@ constructor( private val lockPatternUtils: LockPatternUtils, private val keyguardBypassController: KeyguardBypassController, keyguardStateController: KeyguardStateController, + keyguardRepository: KeyguardRepository, ) : DeviceEntryRepository { + override val enteringDeviceFromBiometricUnlock = + keyguardRepository.biometricUnlockState + .filter { BiometricUnlockModel.dismissesKeyguard(it) } + .sample( + keyguardRepository.biometricUnlockSource.filterNotNull(), + ) private val _isUnlocked = MutableStateFlow(false) diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt new file mode 100644 index 000000000000..1a6bd0427db5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 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.deviceentry.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.deviceentry.shared.DeviceEntryBiometricMode +import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository +import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus +import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map + +/** Business logic for device entry biometric states that may differ based on the biometric mode. */ +@ExperimentalCoroutinesApi +@SysUISingleton +class DeviceEntryBiometricAuthInteractor +@Inject +constructor( + biometricSettingsRepository: BiometricSettingsRepository, + deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor, +) { + private val biometricMode: Flow<DeviceEntryBiometricMode> = + combine( + biometricSettingsRepository.isFingerprintEnrolledAndEnabled, + biometricSettingsRepository.isFaceAuthEnrolledAndEnabled, + ) { fingerprintEnrolled, faceEnrolled -> + if (fingerprintEnrolled && faceEnrolled) { + DeviceEntryBiometricMode.CO_EXPERIENCE + } else if (fingerprintEnrolled) { + DeviceEntryBiometricMode.FINGERPRINT_ONLY + } else if (faceEnrolled) { + DeviceEntryBiometricMode.FACE_ONLY + } else { + DeviceEntryBiometricMode.NONE + } + } + private val faceOnly: Flow<Boolean> = + biometricMode.map { it == DeviceEntryBiometricMode.FACE_ONLY } + + /** + * Triggered if face is the only biometric that can be used for device entry and a face failure + * occurs. + */ + val faceOnlyFaceFailure: Flow<FailedFaceAuthenticationStatus> = + faceOnly.flatMapLatest { faceOnly -> + if (faceOnly) { + deviceEntryFaceAuthInteractor.faceFailure + } else { + emptyFlow() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt new file mode 100644 index 000000000000..70716c6c91fe --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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.deviceentry.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filterIsInstance + +@SysUISingleton +class DeviceEntryFaceAuthInteractor +@Inject +constructor( + repository: DeviceEntryFaceAuthRepository, +) { + val faceFailure: Flow<FailedFaceAuthenticationStatus> = + repository.authenticationStatus.filterIsInstance<FailedFaceAuthenticationStatus>() +} diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt new file mode 100644 index 000000000000..efa1c0a07490 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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.deviceentry.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filterIsInstance + +@SysUISingleton +class DeviceEntryFingerprintAuthInteractor +@Inject +constructor( + repository: DeviceEntryFingerprintAuthRepository, +) { + val fingerprintFailure: Flow<FailFingerprintAuthenticationStatus> = + repository.authenticationStatus.filterIsInstance<FailFingerprintAuthenticationStatus>() +} diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt index 53d6f737af8d..649a9715ffea 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt @@ -19,7 +19,6 @@ import com.android.keyguard.logging.BiometricUnlockLogger import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.deviceentry.data.repository.DeviceEntryHapticsRepository import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.power.domain.interactor.PowerInteractor @@ -34,11 +33,12 @@ import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart /** * Business logic for device entry haptic events. Determines whether the haptic should play. In - * particular, there are extra guards for whether device entry error and successes hatpics should + * particular, there are extra guards for whether device entry error and successes haptics should * play when the physical fingerprint sensor is located on the power button. */ @ExperimentalCoroutinesApi @@ -46,7 +46,9 @@ import kotlinx.coroutines.flow.onStart class DeviceEntryHapticsInteractor @Inject constructor( - private val repository: DeviceEntryHapticsRepository, + deviceEntryInteractor: DeviceEntryInteractor, + deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor, + deviceEntryBiometricAuthInteractor: DeviceEntryBiometricAuthInteractor, fingerprintPropertyRepository: FingerprintPropertyRepository, biometricSettingsRepository: BiometricSettingsRepository, keyEventInteractor: KeyEventInteractor, @@ -77,9 +79,8 @@ constructor( emit(recentPowerButtonPressThresholdMs * -1L - 1L) } - val playSuccessHaptic: Flow<Boolean> = - repository.successHapticRequest - .filter { it } + val playSuccessHaptic: Flow<Unit> = + deviceEntryInteractor.enteringDeviceFromBiometricUnlock .sample( combine( powerButtonSideFpsEnrolled, @@ -88,7 +89,7 @@ constructor( ::Triple ) ) - .map { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) -> + .filter { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) -> val sideFpsAllowsHaptic = !powerButtonDown && systemClock.uptimeMillis() - lastPowerButtonWakeup > @@ -96,38 +97,28 @@ constructor( val allowHaptic = !sideFpsEnrolled || sideFpsAllowsHaptic if (!allowHaptic) { logger.d("Skip success haptic. Recent power button press or button is down.") - handleSuccessHaptic() // immediately handle, don't vibrate } allowHaptic } - val playErrorHaptic: Flow<Boolean> = - repository.errorHapticRequest - .filter { it } + .map {} // map to Unit + + private val playErrorHapticForBiometricFailure: Flow<Unit> = + merge( + deviceEntryFingerprintAuthInteractor.fingerprintFailure, + deviceEntryBiometricAuthInteractor.faceOnlyFaceFailure, + ) + .map {} // map to Unit + val playErrorHaptic: Flow<Unit> = + playErrorHapticForBiometricFailure .sample(combine(powerButtonSideFpsEnrolled, powerButtonDown, ::Pair)) - .map { (sideFpsEnrolled, powerButtonDown) -> + .filter { (sideFpsEnrolled, powerButtonDown) -> val allowHaptic = !sideFpsEnrolled || !powerButtonDown if (!allowHaptic) { logger.d("Skip error haptic. Power button is down.") - handleErrorHaptic() // immediately handle, don't vibrate } allowHaptic } - - fun vibrateSuccess() { - repository.requestSuccessHaptic() - } - - fun vibrateError() { - repository.requestErrorHaptic() - } - - fun handleSuccessHaptic() { - repository.handleSuccessHaptic() - } - - fun handleErrorHaptic() { - repository.handleErrorHaptic() - } + .map {} // map to Unit private val recentPowerButtonPressThresholdMs = 400L } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index 4cddb9ccffdb..47be8ab0c0a2 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -23,6 +23,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.TrustRepository +import com.android.systemui.keyguard.shared.model.BiometricUnlockSource import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey @@ -30,6 +31,7 @@ import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest @@ -60,6 +62,9 @@ constructor( trustRepository: TrustRepository, flags: SceneContainerFlags, ) { + val enteringDeviceFromBiometricUnlock: Flow<BiometricUnlockSource> = + repository.enteringDeviceFromBiometricUnlock + /** * Whether the device is unlocked. * diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryBiometricMode.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryBiometricMode.kt new file mode 100644 index 000000000000..6d885b349527 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryBiometricMode.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 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.deviceentry.shared + +/** Models the biometrics that can be used to enter the device. */ +enum class DeviceEntryBiometricMode { + /** No biometrics can be used to enter the device from the lockscreen. */ + NONE, + /** Only face can be used to enter the device from the lockscreen. */ + FACE_ONLY, + /** Only fingerprint can be used to enter the device from the lockscreen. */ + FINGERPRINT_ONLY, + /** Both face and fingerprint can be used to enter the device from the lockscreen. */ + CO_EXPERIENCE, +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt index 8fe6309fc005..2ae5ce1ad545 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt @@ -51,9 +51,21 @@ enum class BiometricUnlockModel { companion object { private val wakeAndUnlockModes = setOf(WAKE_AND_UNLOCK, WAKE_AND_UNLOCK_FROM_DREAM, WAKE_AND_UNLOCK_PULSING) + private val dismissesKeyguardModes = + setOf( + WAKE_AND_UNLOCK, + WAKE_AND_UNLOCK_PULSING, + UNLOCK_COLLAPSING, + WAKE_AND_UNLOCK_FROM_DREAM, + DISMISS_BOUNCER + ) fun isWakeAndUnlock(model: BiometricUnlockModel): Boolean { return wakeAndUnlockModes.contains(model) } + + fun dismissesKeyguard(model: BiometricUnlockModel): Boolean { + return dismissesKeyguardModes.contains(model) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt index 3c143fe1a68a..cc385a8eea85 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt @@ -49,7 +49,7 @@ data class AcquiredFingerprintAuthenticationStatus(val acquiredInfo: Int) : } /** Fingerprint authentication failed message. */ -object FailFingerprintAuthenticationStatus : FingerprintAuthenticationStatus() +data object FailFingerprintAuthenticationStatus : FingerprintAuthenticationStatus() /** Fingerprint authentication error message */ data class ErrorFingerprintAuthenticationStatus( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt index 4b4a19ecd770..dcf4284438bf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.binder import android.annotation.SuppressLint import android.content.res.ColorStateList +import android.view.HapticFeedbackConstants import android.view.View import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle @@ -30,6 +31,7 @@ import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.FalsingManager +import com.android.systemui.statusbar.VibratorHelper import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch @@ -51,6 +53,7 @@ object DeviceEntryIconViewBinder { fgViewModel: DeviceEntryForegroundViewModel, bgViewModel: DeviceEntryBackgroundViewModel, falsingManager: FalsingManager, + vibratorHelper: VibratorHelper, ) { DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode() val longPressHandlingView = view.longPressHandlingView @@ -62,6 +65,10 @@ object DeviceEntryIconViewBinder { if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) { return } + vibratorHelper.performHapticFeedback( + view, + HapticFeedbackConstants.CONFIRM, + ) viewModel.onLongPress() } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index 0addbd8e18b2..e5713d67ce7e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -65,7 +65,6 @@ import javax.inject.Provider import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -238,27 +237,21 @@ object KeyguardRootViewBinder { if (deviceEntryHapticsInteractor != null && vibratorHelper != null) { launch { - deviceEntryHapticsInteractor.playSuccessHaptic - .filter { it } - .collect { - vibratorHelper.performHapticFeedback( - view, - HapticFeedbackConstants.CONFIRM, - ) - deviceEntryHapticsInteractor.handleSuccessHaptic() - } + deviceEntryHapticsInteractor.playSuccessHaptic.collect { + vibratorHelper.performHapticFeedback( + view, + HapticFeedbackConstants.CONFIRM, + ) + } } launch { - deviceEntryHapticsInteractor.playErrorHaptic - .filter { it } - .collect { - vibratorHelper.performHapticFeedback( - view, - HapticFeedbackConstants.REJECT, - ) - deviceEntryHapticsInteractor.handleErrorHaptic() - } + deviceEntryHapticsInteractor.playErrorHaptic.collect { + vibratorHelper.performHapticFeedback( + view, + HapticFeedbackConstants.REJECT, + ) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt index 77ab9f416910..a693ec9317d0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt @@ -49,6 +49,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.res.R import com.android.systemui.shade.NotificationPanelView import com.android.systemui.statusbar.NotificationShadeWindowController +import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.gesture.TapGestureDetector import dagger.Lazy import javax.inject.Inject @@ -76,6 +77,7 @@ constructor( @Application private val scope: CoroutineScope, private val swipeUpAnywhereGestureHandler: Lazy<SwipeUpAnywhereGestureHandler>, private val tapGestureDetector: Lazy<TapGestureDetector>, + private val vibratorHelper: Lazy<VibratorHelper>, ) : KeyguardSection() { private val deviceEntryIconViewId = R.id.device_entry_icon_view private val alternateBouncerViewId = R.id.alternate_bouncer @@ -114,6 +116,7 @@ constructor( deviceEntryForegroundViewModel.get(), deviceEntryBackgroundViewModel.get(), falsingManager.get(), + vibratorHelper.get(), ) } constraintLayout.findViewById<FrameLayout?>(alternateBouncerViewId)?.let { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt index bd6aae8f2dcb..f95713bf1802 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import android.animation.FloatEvaluator import android.animation.IntEvaluator import com.android.keyguard.KeyguardViewController -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.BurnInInteractor @@ -56,7 +55,6 @@ constructor( val shadeDependentFlows: ShadeDependentFlows, private val sceneContainerFlags: SceneContainerFlags, private val keyguardViewController: Lazy<KeyguardViewController>, - private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor, private val deviceEntryInteractor: DeviceEntryInteractor, ) { private val intEvaluator = IntEvaluator() @@ -182,8 +180,6 @@ constructor( } fun onLongPress() { - deviceEntryHapticsInteractor.vibrateSuccess() - // TODO (b/309804148): play auth ripple via an interactor if (sceneContainerFlags.isEnabled()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 4e77801af515..97fc35a062f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -47,7 +47,6 @@ import com.android.systemui.Dumpable; import com.android.systemui.biometrics.AuthController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WakefulnessLifecycle; @@ -180,8 +179,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private final SystemClock mSystemClock; private final boolean mOrderUnlockAndWake; private final Lazy<SelectedUserInteractor> mSelectedUserInteractor; - private final DeviceEntryHapticsInteractor mHapticsInteractor; - private long mLastFpFailureUptimeMillis; private int mNumConsecutiveFpFailures; @@ -288,7 +285,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp ScreenOffAnimationController screenOffAnimationController, VibratorHelper vibrator, SystemClock systemClock, - DeviceEntryHapticsInteractor hapticsInteractor, Lazy<SelectedUserInteractor> selectedUserInteractor, BiometricUnlockInteractor biometricUnlockInteractor ) { @@ -320,7 +316,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mSystemClock = systemClock; mOrderUnlockAndWake = resources.getBoolean( com.android.internal.R.bool.config_orderUnlockAndWake); - mHapticsInteractor = hapticsInteractor; mSelectedUserInteractor = selectedUserInteractor; dumpManager.registerDumpable(this); @@ -442,7 +437,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp if (mode == MODE_WAKE_AND_UNLOCK || mode == MODE_WAKE_AND_UNLOCK_PULSING || mode == MODE_UNLOCK_COLLAPSING || mode == MODE_WAKE_AND_UNLOCK_FROM_DREAM || mode == MODE_DISMISS_BOUNCER) { - mHapticsInteractor.vibrateSuccess(); onBiometricUnlockedWithKeyguardDismissal(biometricSourceType); } startWakeAndUnlock(mode); @@ -726,14 +720,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp } } - // Suppress all face auth errors if fingerprint can be used to authenticate - if ((biometricSourceType == BiometricSourceType.FACE - && !mUpdateMonitor.isUnlockWithFingerprintPossible( - mSelectedUserInteractor.get().getSelectedUserId())) - || (biometricSourceType == BiometricSourceType.FINGERPRINT)) { - mHapticsInteractor.vibrateError(); - } - cleanup(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt index 9b8e581d1ba4..59bcf01e0eef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt @@ -18,157 +18,165 @@ package com.android.systemui.deviceentry.data.repository import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.keyguard.logging.BiometricUnlockLogger import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository +import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength -import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor -import com.android.systemui.keyevent.data.repository.FakeKeyEventRepository -import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor -import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.power.data.repository.FakePowerRepository -import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor +import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository +import com.android.systemui.keyguard.data.repository.biometricSettingsRepository +import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.shared.model.BiometricUnlockSource +import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus +import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus +import com.android.systemui.kosmos.testScope +import com.android.systemui.power.data.repository.powerRepository import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState -import com.android.systemui.statusbar.phone.ScreenOffAnimationController -import com.android.systemui.util.time.FakeSystemClock +import com.android.systemui.testKosmos +import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.mock @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class DeviceEntryHapticsInteractorTest : SysuiTestCase() { - - private lateinit var repository: DeviceEntryHapticsRepository - private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository - private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository - private lateinit var keyEventRepository: FakeKeyEventRepository - private lateinit var powerRepository: FakePowerRepository - private lateinit var systemClock: FakeSystemClock - private lateinit var underTest: DeviceEntryHapticsInteractor - - @Before - fun setUp() { - repository = DeviceEntryHapticsRepositoryImpl() - fingerprintPropertyRepository = FakeFingerprintPropertyRepository() - biometricSettingsRepository = FakeBiometricSettingsRepository() - keyEventRepository = FakeKeyEventRepository() - powerRepository = FakePowerRepository() - systemClock = FakeSystemClock() - underTest = - DeviceEntryHapticsInteractor( - repository = repository, - fingerprintPropertyRepository = fingerprintPropertyRepository, - biometricSettingsRepository = biometricSettingsRepository, - keyEventInteractor = KeyEventInteractor(keyEventRepository), - powerInteractor = - PowerInteractor( - powerRepository, - mock(FalsingCollector::class.java), - mock(ScreenOffAnimationController::class.java), - mock(StatusBarStateController::class.java), - ), - systemClock = systemClock, - logger = mock(BiometricUnlockLogger::class.java), - ) - } + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val underTest = kosmos.deviceEntryHapticsInteractor @Test - fun nonPowerButtonFPS_vibrateSuccess() = runTest { - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) - setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC) - underTest.vibrateSuccess() - assertThat(playSuccessHaptic).isTrue() - } + fun nonPowerButtonFPS_vibrateSuccess() = + testScope.runTest { + val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC) + runCurrent() + enterDeviceFromBiometricUnlock() + assertThat(playSuccessHaptic).isNotNull() + } @Test - fun powerButtonFPS_vibrateSuccess() = runTest { - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) - setPowerButtonFingerprintProperty() - setFingerprintEnrolled() - keyEventRepository.setPowerButtonDown(false) - - // It's been 10 seconds since the last power button wakeup - setAwakeFromPowerButton() - runCurrent() - systemClock.setUptimeMillis(systemClock.uptimeMillis() + 10000) - - underTest.vibrateSuccess() - assertThat(playSuccessHaptic).isTrue() - } + fun powerButtonFPS_vibrateSuccess() = + testScope.runTest { + val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + setPowerButtonFingerprintProperty() + setFingerprintEnrolled() + kosmos.fakeKeyEventRepository.setPowerButtonDown(false) + + // It's been 10 seconds since the last power button wakeup + setAwakeFromPowerButton() + runCurrent() + kosmos.fakeSystemClock.setUptimeMillis(kosmos.fakeSystemClock.uptimeMillis() + 10000) + + enterDeviceFromBiometricUnlock() + assertThat(playSuccessHaptic).isNotNull() + } @Test - fun powerButtonFPS_powerDown_doNotVibrateSuccess() = runTest { - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) - setPowerButtonFingerprintProperty() - setFingerprintEnrolled() - keyEventRepository.setPowerButtonDown(true) // power button is currently DOWN - - // It's been 10 seconds since the last power button wakeup - setAwakeFromPowerButton() - runCurrent() - systemClock.setUptimeMillis(systemClock.uptimeMillis() + 10000) + fun powerButtonFPS_powerDown_doNotVibrateSuccess() = + testScope.runTest { + val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + setPowerButtonFingerprintProperty() + setFingerprintEnrolled() + kosmos.fakeKeyEventRepository.setPowerButtonDown(true) // power button is currently DOWN + + // It's been 10 seconds since the last power button wakeup + setAwakeFromPowerButton() + runCurrent() + kosmos.fakeSystemClock.setUptimeMillis(kosmos.fakeSystemClock.uptimeMillis() + 10000) + + enterDeviceFromBiometricUnlock() + assertThat(playSuccessHaptic).isNull() + } - underTest.vibrateSuccess() - assertThat(playSuccessHaptic).isFalse() - } + @Test + fun powerButtonFPS_powerButtonRecentlyPressed_doNotVibrateSuccess() = + testScope.runTest { + val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + setPowerButtonFingerprintProperty() + setFingerprintEnrolled() + kosmos.fakeKeyEventRepository.setPowerButtonDown(false) + + // It's only been 50ms since the last power button wakeup + setAwakeFromPowerButton() + runCurrent() + kosmos.fakeSystemClock.setUptimeMillis(kosmos.fakeSystemClock.uptimeMillis() + 50) + + enterDeviceFromBiometricUnlock() + assertThat(playSuccessHaptic).isNull() + } @Test - fun powerButtonFPS_powerButtonRecentlyPressed_doNotVibrateSuccess() = runTest { - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) - setPowerButtonFingerprintProperty() - setFingerprintEnrolled() - keyEventRepository.setPowerButtonDown(false) + fun nonPowerButtonFPS_vibrateError() = + testScope.runTest { + val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) + setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC) + runCurrent() + fingerprintFailure() + assertThat(playErrorHaptic).isNotNull() + } - // It's only been 50ms since the last power button wakeup - setAwakeFromPowerButton() - runCurrent() - systemClock.setUptimeMillis(systemClock.uptimeMillis() + 50) + @Test + fun nonPowerButtonFPS_coExFaceFailure_doNotVibrateError() = + testScope.runTest { + val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) + setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC) + coExEnrolledAndEnabled() + runCurrent() + faceFailure() + assertThat(playErrorHaptic).isNull() + } - underTest.vibrateSuccess() - assertThat(playSuccessHaptic).isFalse() - } + @Test + fun powerButtonFPS_vibrateError() = + testScope.runTest { + val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) + setPowerButtonFingerprintProperty() + setFingerprintEnrolled() + runCurrent() + fingerprintFailure() + assertThat(playErrorHaptic).isNotNull() + } @Test - fun nonPowerButtonFPS_vibrateError() = runTest { - val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) - setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC) - underTest.vibrateError() - assertThat(playErrorHaptic).isTrue() + fun powerButtonFPS_powerDown_doNotVibrateError() = + testScope.runTest { + val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) + setPowerButtonFingerprintProperty() + setFingerprintEnrolled() + kosmos.fakeKeyEventRepository.setPowerButtonDown(true) + runCurrent() + fingerprintFailure() + assertThat(playErrorHaptic).isNull() + } + + private suspend fun enterDeviceFromBiometricUnlock() { + kosmos.fakeDeviceEntryRepository.enteringDeviceFromBiometricUnlock( + BiometricUnlockSource.FINGERPRINT_SENSOR + ) } - @Test - fun powerButtonFPS_vibrateError() = runTest { - val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) - setPowerButtonFingerprintProperty() - setFingerprintEnrolled() - underTest.vibrateError() - assertThat(playErrorHaptic).isTrue() + private fun fingerprintFailure() { + kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus( + FailFingerprintAuthenticationStatus + ) } - @Test - fun powerButtonFPS_powerDown_doNotVibrateError() = runTest { - val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) - setPowerButtonFingerprintProperty() - setFingerprintEnrolled() - keyEventRepository.setPowerButtonDown(true) - underTest.vibrateError() - assertThat(playErrorHaptic).isFalse() + private fun faceFailure() { + kosmos.fakeDeviceEntryFaceAuthRepository.setAuthenticationStatus( + FailedFaceAuthenticationStatus() + ) } private fun setFingerprintSensorType(fingerprintSensorType: FingerprintSensorType) { - fingerprintPropertyRepository.setProperties( + kosmos.fingerprintPropertyRepository.setProperties( sensorId = 0, strength = SensorStrength.STRONG, sensorType = fingerprintSensorType, @@ -181,15 +189,20 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() { } private fun setFingerprintEnrolled() { - biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) + kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) } private fun setAwakeFromPowerButton() { - powerRepository.updateWakefulness( + kosmos.powerRepository.updateWakefulness( WakefulnessState.AWAKE, WakeSleepReason.POWER_BUTTON, WakeSleepReason.POWER_BUTTON, powerButtonLaunchGestureTriggered = false, ) } + + private fun coExEnrolledAndEnabled() { + setFingerprintEnrolled() + kosmos.biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt index 67fba42aac5b..22569e27d02a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt @@ -39,6 +39,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.res.R import com.android.systemui.shade.NotificationPanelView import com.android.systemui.statusbar.NotificationShadeWindowController +import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.gesture.TapGestureDetector import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -91,6 +92,7 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() { TestScope().backgroundScope, { mock(SwipeUpAnywhereGestureHandler::class.java) }, { mock(TapGestureDetector::class.java) }, + { mock(VibratorHelper::class.java) }, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index 051a4c1c05cd..6f65eb426e64 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -47,7 +47,6 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.logging.BiometricUnlockLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WakefulnessLifecycle; @@ -122,8 +121,6 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { @Mock private ViewRootImpl mViewRootImpl; @Mock - private DeviceEntryHapticsInteractor mDeviceEntryHapticsInteractor; - @Mock private SelectedUserInteractor mSelectedUserInteractor; @Mock private BiometricUnlockInteractor mBiometricUnlockInteractor; @@ -160,7 +157,6 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mAuthController, mStatusBarStateController, mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper, mSystemClock, - mDeviceEntryHapticsInteractor, () -> mSelectedUserInteractor, mBiometricUnlockInteractor ); @@ -466,26 +462,6 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { } @Test - public void onFingerprintSuccess_requestSuccessHaptic() { - // WHEN biometric fingerprint succeeds - givenFingerprintModeUnlockCollapsing(); - mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT, - true); - - // THEN always vibrate the device - verify(mDeviceEntryHapticsInteractor).vibrateSuccess(); - } - - @Test - public void onFingerprintFail_requestErrorHaptic() { - // WHEN biometric fingerprint fails - mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); - - // THEN always vibrate the device - verify(mDeviceEntryHapticsInteractor).vibrateError(); - } - - @Test public void onFingerprintDetect_showBouncer() { // WHEN fingerprint detect occurs mBiometricUnlockController.onBiometricDetected(UserHandle.USER_CURRENT, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt index d97cb56787cc..8ff04a63802a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt @@ -16,7 +16,6 @@ package com.android.systemui.deviceentry.data import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepositoryModule -import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryHapticsRepositoryModule import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepositoryModule import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepositoryModule import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepositoryModule @@ -29,7 +28,6 @@ import dagger.Module [ FakeBiometricSettingsRepositoryModule::class, FakeDeviceEntryRepositoryModule::class, - FakeDeviceEntryHapticsRepositoryModule::class, FakeDeviceEntryFaceAuthRepositoryModule::class, FakeDeviceEntryFingerprintAuthRepositoryModule::class, FakeFingerprintPropertyRepositoryModule::class, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryHapticsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryHapticsRepository.kt deleted file mode 100644 index b29b67d7c220..000000000000 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryHapticsRepository.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2023 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.deviceentry.data.repository - -import com.android.systemui.dagger.SysUISingleton -import dagger.Binds -import dagger.Module -import javax.inject.Inject -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow - -/** Fake implementation of [DeviceEntryHapticsRepository] */ -@SysUISingleton -class FakeDeviceEntryHapticsRepository @Inject constructor() : DeviceEntryHapticsRepository { - private var _successHapticRequest: MutableStateFlow<Boolean> = MutableStateFlow(false) - override val successHapticRequest: Flow<Boolean> = _successHapticRequest.asStateFlow() - - private var _errorHapticRequest: MutableStateFlow<Boolean> = MutableStateFlow(false) - override val errorHapticRequest: Flow<Boolean> = _errorHapticRequest.asStateFlow() - - override fun requestSuccessHaptic() { - _successHapticRequest.value = true - } - - override fun handleSuccessHaptic() { - _successHapticRequest.value = false - } - - override fun requestErrorHaptic() { - _errorHapticRequest.value = true - } - - override fun handleErrorHaptic() { - _errorHapticRequest.value = false - } -} - -@Module -interface FakeDeviceEntryHapticsRepositoryModule { - @Binds fun bindFake(fake: FakeDeviceEntryHapticsRepository): DeviceEntryHapticsRepository -} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt index ba70d46fd954..6436a382eb7f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt @@ -16,16 +16,24 @@ package com.android.systemui.deviceentry.data.repository import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.shared.model.BiometricUnlockSource import dagger.Binds import dagger.Module import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow /** Fake implementation of [DeviceEntryRepository] */ @SysUISingleton class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository { + private val _enteringDeviceFromBiometricUnlock: MutableSharedFlow<BiometricUnlockSource> = + MutableSharedFlow() + override val enteringDeviceFromBiometricUnlock: Flow<BiometricUnlockSource> = + _enteringDeviceFromBiometricUnlock.asSharedFlow() private var isLockscreenEnabled = true @@ -54,6 +62,10 @@ class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository { fun setBypassEnabled(isBypassEnabled: Boolean) { _isBypassEnabled.value = isBypassEnabled } + + suspend fun enteringDeviceFromBiometricUnlock(sourceType: BiometricUnlockSource) { + _enteringDeviceFromBiometricUnlock.emit(sourceType) + } } @Module diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractorKosmos.kt new file mode 100644 index 000000000000..1bd105620813 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractorKosmos.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.deviceentry.domain.interactor + +import com.android.systemui.keyguard.data.repository.biometricSettingsRepository +import com.android.systemui.kosmos.Kosmos +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.deviceEntryBiometricAuthInteractor by + Kosmos.Fixture { + DeviceEntryBiometricAuthInteractor( + biometricSettingsRepository = biometricSettingsRepository, + deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt index 8bb07d9487cd..d2dff7834b40 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt @@ -14,10 +14,17 @@ * limitations under the License. */ -package com.android.systemui.deviceentry.data.repository +@file:OptIn(ExperimentalCoroutinesApi::class) +package com.android.systemui.deviceentry.domain.interactor + +import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository import com.android.systemui.kosmos.Kosmos +import kotlinx.coroutines.ExperimentalCoroutinesApi -var Kosmos.deviceEntryHapticsRepository: DeviceEntryHapticsRepository by - Kosmos.Fixture { fakeDeviceEntryHapticsRepository } -val Kosmos.fakeDeviceEntryHapticsRepository by Kosmos.Fixture { FakeDeviceEntryHapticsRepository() } +val Kosmos.deviceEntryFaceAuthInteractor by + Kosmos.Fixture { + DeviceEntryFaceAuthInteractor( + repository = deviceEntryFaceAuthRepository, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorKosmos.kt new file mode 100644 index 000000000000..66c6f86c452d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorKosmos.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.deviceentry.domain.interactor + +import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository +import com.android.systemui.kosmos.Kosmos +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.deviceEntryFingerprintAuthInteractor by + Kosmos.Fixture { + DeviceEntryFingerprintAuthInteractor( + repository = deviceEntryFingerprintAuthRepository, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt index de6cacb00faa..6bf527df4026 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt @@ -20,7 +20,6 @@ package com.android.systemui.deviceentry.domain.interactor import com.android.keyguard.logging.biometricUnlockLogger import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository -import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryHapticsRepository import com.android.systemui.keyevent.domain.interactor.keyEventInteractor import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.kosmos.Kosmos @@ -31,7 +30,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.deviceEntryHapticsInteractor by Kosmos.Fixture { DeviceEntryHapticsInteractor( - repository = fakeDeviceEntryHapticsRepository, + deviceEntryInteractor = deviceEntryInteractor, + deviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor, + deviceEntryBiometricAuthInteractor = deviceEntryBiometricAuthInteractor, fingerprintPropertyRepository = fingerprintPropertyRepository, biometricSettingsRepository = biometricSettingsRepository, keyEventInteractor = keyEventInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt index 299262b91027..6557bcfd8337 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt @@ -16,7 +16,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.burnInInteractor @@ -45,7 +44,6 @@ val Kosmos.deviceEntryIconViewModel by Fixture { shadeDependentFlows = shadeDependentFlows, sceneContainerFlags = sceneContainerFlags, keyguardViewController = { statusBarKeyguardViewManager }, - deviceEntryHapticsInteractor = deviceEntryHapticsInteractor, deviceEntryInteractor = deviceEntryInteractor, ) } |