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