• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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