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