1 package com.android.systemui.deviceentry.data.repository 2 3 import com.android.internal.widget.LockPatternUtils 4 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow 5 import com.android.systemui.dagger.SysUISingleton 6 import com.android.systemui.dagger.qualifiers.Application 7 import com.android.systemui.dagger.qualifiers.Background 8 import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus 9 import com.android.systemui.statusbar.phone.KeyguardBypassController 10 import com.android.systemui.user.data.repository.UserRepository 11 import dagger.Binds 12 import dagger.Module 13 import javax.inject.Inject 14 import kotlinx.coroutines.CoroutineDispatcher 15 import kotlinx.coroutines.CoroutineScope 16 import kotlinx.coroutines.channels.awaitClose 17 import kotlinx.coroutines.flow.MutableStateFlow 18 import kotlinx.coroutines.flow.SharingStarted 19 import kotlinx.coroutines.flow.StateFlow 20 import kotlinx.coroutines.flow.asStateFlow 21 import kotlinx.coroutines.flow.stateIn 22 import kotlinx.coroutines.withContext 23 24 /** Interface for classes that can access device-entry-related application state. */ 25 interface DeviceEntryRepository { 26 /** 27 * Whether the lockscreen is enabled for the current user. This is `true` whenever the user has 28 * chosen any secure authentication method and even if they set the lockscreen to be dismissed 29 * when the user swipes on it. 30 */ 31 val isLockscreenEnabled: StateFlow<Boolean> 32 33 /** 34 * Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically 35 * dismissed once the authentication challenge is completed. 36 * 37 * This is a setting that is specific to the face unlock authentication method, because the user 38 * intent to unlock is not known. On devices that don't support face unlock, this always returns 39 * `true`. 40 * 41 * When this is `false`, an automatically-triggered face unlock shouldn't automatically dismiss 42 * the lockscreen. 43 */ 44 val isBypassEnabled: StateFlow<Boolean> 45 46 val deviceUnlockStatus: MutableStateFlow<DeviceUnlockStatus> 47 48 /** 49 * Whether the lockscreen is enabled for the current user. This is `true` whenever the user has 50 * chosen any secure authentication method and even if they set the lockscreen to be dismissed 51 * when the user swipes on it. 52 */ isLockscreenEnablednull53 suspend fun isLockscreenEnabled(): Boolean 54 } 55 56 /** Encapsulates application state for device entry. */ 57 @SysUISingleton 58 class DeviceEntryRepositoryImpl 59 @Inject 60 constructor( 61 @Application private val applicationScope: CoroutineScope, 62 @Background private val backgroundDispatcher: CoroutineDispatcher, 63 private val userRepository: UserRepository, 64 private val lockPatternUtils: LockPatternUtils, 65 private val keyguardBypassController: KeyguardBypassController, 66 ) : DeviceEntryRepository { 67 68 private val _isLockscreenEnabled = MutableStateFlow(true) 69 override val isLockscreenEnabled: StateFlow<Boolean> = _isLockscreenEnabled.asStateFlow() 70 71 override val isBypassEnabled: StateFlow<Boolean> = 72 conflatedCallbackFlow { 73 val listener = 74 object : KeyguardBypassController.OnBypassStateChangedListener { 75 override fun onBypassStateChanged(isEnabled: Boolean) { 76 trySend(isEnabled) 77 } 78 } 79 keyguardBypassController.registerOnBypassStateChangedListener(listener) 80 awaitClose { 81 keyguardBypassController.unregisterOnBypassStateChangedListener(listener) 82 } 83 } 84 .stateIn( 85 applicationScope, 86 SharingStarted.Eagerly, 87 initialValue = keyguardBypassController.bypassEnabled, 88 ) 89 90 override val deviceUnlockStatus = 91 MutableStateFlow(DeviceUnlockStatus(isUnlocked = false, deviceUnlockSource = null)) 92 93 override suspend fun isLockscreenEnabled(): Boolean { 94 return withContext(backgroundDispatcher) { 95 val selectedUserId = userRepository.getSelectedUserInfo().id 96 val isEnabled = !lockPatternUtils.isLockScreenDisabled(selectedUserId) 97 _isLockscreenEnabled.value = isEnabled 98 isEnabled 99 } 100 } 101 } 102 103 @Module 104 interface DeviceEntryRepositoryModule { repositorynull105 @Binds fun repository(impl: DeviceEntryRepositoryImpl): DeviceEntryRepository 106 } 107