Loading packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading @@ -55,6 +57,7 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { lockPatternUtils = lockPatternUtils, keyguardBypassController = keyguardBypassController, keyguardStateController = keyguardStateController, keyguardRepository = keyguardRepository, ) testScope.runCurrent() } Loading packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt +0 −2 Original line number Diff line number Diff line 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 Loading @@ -10,7 +9,6 @@ import dagger.multibindings.Multibinds includes = [ DeviceEntryRepositoryModule::class, DeviceEntryHapticsRepositoryModule::class, ], ) abstract class DeviceEntryModule { Loading packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt +17 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading Loading @@ -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) Loading packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * 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() } } } packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryHapticsRepository.kt→packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt +34 −0 Original line number Diff line number Diff line Loading @@ -13,43 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.deviceentry.data.repository package com.android.systemui.deviceentry.domain.interactor import com.android.systemui.dagger.SysUISingleton import dagger.Binds import dagger.Module 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.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filterIsInstance /** 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 class DeviceEntryFaceAuthInteractor @Inject constructor( repository: DeviceEntryFaceAuthRepository, ) { val faceFailure: Flow<FailedFaceAuthenticationStatus> = repository.authenticationStatus.filterIsInstance<FailedFaceAuthenticationStatus>() } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading @@ -55,6 +57,7 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { lockPatternUtils = lockPatternUtils, keyguardBypassController = keyguardBypassController, keyguardStateController = keyguardStateController, keyguardRepository = keyguardRepository, ) testScope.runCurrent() } Loading
packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt +0 −2 Original line number Diff line number Diff line 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 Loading @@ -10,7 +9,6 @@ import dagger.multibindings.Multibinds includes = [ DeviceEntryRepositoryModule::class, DeviceEntryHapticsRepositoryModule::class, ], ) abstract class DeviceEntryModule { Loading
packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt +17 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading Loading @@ -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) Loading
packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * 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() } } }
packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryHapticsRepository.kt→packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt +34 −0 Original line number Diff line number Diff line Loading @@ -13,43 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.deviceentry.data.repository package com.android.systemui.deviceentry.domain.interactor import com.android.systemui.dagger.SysUISingleton import dagger.Binds import dagger.Module 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.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filterIsInstance /** 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 class DeviceEntryFaceAuthInteractor @Inject constructor( repository: DeviceEntryFaceAuthRepository, ) { val faceFailure: Flow<FailedFaceAuthenticationStatus> = repository.authenticationStatus.filterIsInstance<FailedFaceAuthenticationStatus>() }