1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.systemui.bouncer.data.repository
18 
19 import android.os.Build
20 import android.util.Log
21 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
22 import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel
23 import com.android.systemui.dagger.SysUISingleton
24 import com.android.systemui.dagger.qualifiers.Application
25 import com.android.systemui.log.dagger.BouncerTableLog
26 import com.android.systemui.log.table.TableLogBuffer
27 import com.android.systemui.log.table.logDiffsForTable
28 import com.android.systemui.util.time.SystemClock
29 import javax.inject.Inject
30 import kotlinx.coroutines.CoroutineScope
31 import kotlinx.coroutines.flow.MutableStateFlow
32 import kotlinx.coroutines.flow.StateFlow
33 import kotlinx.coroutines.flow.asStateFlow
34 import kotlinx.coroutines.flow.filterNotNull
35 import kotlinx.coroutines.flow.launchIn
36 import kotlinx.coroutines.flow.map
37 import kotlinx.coroutines.flow.onEach
38 
39 /**
40  * Encapsulates app state for the lock screen primary and alternate bouncer.
41  *
42  * Make sure to add newly added flows to the logger.
43  */
44 interface KeyguardBouncerRepository {
45     /** Values associated with the PrimaryBouncer (pin/pattern/password) input. */
46     val primaryBouncerShow: StateFlow<Boolean>
47     val primaryBouncerShowingSoon: StateFlow<Boolean>
48     val primaryBouncerStartingToHide: StateFlow<Boolean>
49     val primaryBouncerStartingDisappearAnimation: StateFlow<Runnable?>
50     /** Determines if we want to instantaneously show the primary bouncer instead of translating. */
51     val primaryBouncerScrimmed: StateFlow<Boolean>
52     /**
53      * Set how much of the notification panel is showing on the screen.
54      *
55      * ```
56      *      0f = panel fully hidden = bouncer fully showing
57      *      1f = panel fully showing = bouncer fully hidden
58      * ```
59      */
60     val panelExpansionAmount: StateFlow<Float>
61     val keyguardPosition: StateFlow<Float?>
62     val isBackButtonEnabled: StateFlow<Boolean?>
63     /** Determines if user is already unlocked */
64     val keyguardAuthenticated: StateFlow<Boolean?>
65     val showMessage: StateFlow<BouncerShowMessageModel?>
66     val resourceUpdateRequests: StateFlow<Boolean>
67     val alternateBouncerVisible: StateFlow<Boolean>
68     val alternateBouncerUIAvailable: StateFlow<Boolean>
69     val sideFpsShowing: StateFlow<Boolean>
70 
71     var lastAlternateBouncerVisibleTime: Long
72 
73     fun setPrimaryScrimmed(isScrimmed: Boolean)
74 
75     fun setPrimaryShow(isShowing: Boolean)
76 
77     fun setPrimaryShowingSoon(showingSoon: Boolean)
78 
79     fun setPrimaryStartingToHide(startingToHide: Boolean)
80 
81     fun setPrimaryStartDisappearAnimation(runnable: Runnable?)
82 
83     fun setPanelExpansion(panelExpansion: Float)
84 
85     fun setKeyguardPosition(keyguardPosition: Float)
86 
87     fun setResourceUpdateRequests(willUpdateResources: Boolean)
88 
89     fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?)
90 
91     fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?)
92 
93     fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean)
94 
95     fun setAlternateVisible(isVisible: Boolean)
96 
97     fun setAlternateBouncerUIAvailable(isAvailable: Boolean)
98 
99     fun setSideFpsShowing(isShowing: Boolean)
100 }
101 
102 @SysUISingleton
103 class KeyguardBouncerRepositoryImpl
104 @Inject
105 constructor(
106     private val clock: SystemClock,
107     @Application private val applicationScope: CoroutineScope,
108     @BouncerTableLog private val buffer: TableLogBuffer,
109 ) : KeyguardBouncerRepository {
110     /** Values associated with the PrimaryBouncer (pin/pattern/password) input. */
111     private val _primaryBouncerShow = MutableStateFlow(false)
112     override val primaryBouncerShow = _primaryBouncerShow.asStateFlow()
113     private val _primaryBouncerShowingSoon = MutableStateFlow(false)
114     override val primaryBouncerShowingSoon = _primaryBouncerShowingSoon.asStateFlow()
115     private val _primaryBouncerStartingToHide = MutableStateFlow(false)
116     override val primaryBouncerStartingToHide = _primaryBouncerStartingToHide.asStateFlow()
117     private val _primaryBouncerDisappearAnimation = MutableStateFlow<Runnable?>(null)
118     override val primaryBouncerStartingDisappearAnimation =
119         _primaryBouncerDisappearAnimation.asStateFlow()
120     /** Determines if we want to instantaneously show the primary bouncer instead of translating. */
121     private val _primaryBouncerScrimmed = MutableStateFlow(false)
122     override val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow()
123     /**
124      * Set how much of the notification panel is showing on the screen.
125      *
126      * ```
127      *      0f = panel fully hidden = bouncer fully showing
128      *      1f = panel fully showing = bouncer fully hidden
129      * ```
130      */
131     private val _panelExpansionAmount = MutableStateFlow(EXPANSION_HIDDEN)
132     override val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
133     private val _keyguardPosition = MutableStateFlow<Float?>(null)
134     override val keyguardPosition = _keyguardPosition.asStateFlow()
135     private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
136     override val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
137     private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null)
138     /** Determines if user is already unlocked */
139     override val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
140     private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null)
141     override val showMessage = _showMessage.asStateFlow()
142     private val _resourceUpdateRequests = MutableStateFlow(false)
143     override val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
144     /** Values associated with the AlternateBouncer */
145     private val _alternateBouncerVisible = MutableStateFlow(false)
146     override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow()
147     override var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE
148     private val _alternateBouncerUIAvailable = MutableStateFlow(false)
149     override val alternateBouncerUIAvailable: StateFlow<Boolean> =
150         _alternateBouncerUIAvailable.asStateFlow()
151     private val _sideFpsShowing = MutableStateFlow(false)
152     override val sideFpsShowing: StateFlow<Boolean> = _sideFpsShowing.asStateFlow()
153 
154     init {
155         setUpLogging()
156     }
157 
158     override fun setPrimaryScrimmed(isScrimmed: Boolean) {
159         _primaryBouncerScrimmed.value = isScrimmed
160     }
161 
162     override fun setAlternateVisible(isVisible: Boolean) {
163         if (isVisible && !_alternateBouncerVisible.value) {
164             lastAlternateBouncerVisibleTime = clock.uptimeMillis()
165         } else if (!isVisible) {
166             lastAlternateBouncerVisibleTime = NOT_VISIBLE
167         }
168         _alternateBouncerVisible.value = isVisible
169     }
170 
171     override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
172         _alternateBouncerUIAvailable.value = isAvailable
173     }
174 
175     override fun setPrimaryShow(isShowing: Boolean) {
176         _primaryBouncerShow.value = isShowing
177     }
178 
179     override fun setPrimaryShowingSoon(showingSoon: Boolean) {
180         _primaryBouncerShowingSoon.value = showingSoon
181     }
182 
183     override fun setPrimaryStartingToHide(startingToHide: Boolean) {
184         _primaryBouncerStartingToHide.value = startingToHide
185     }
186 
187     override fun setPrimaryStartDisappearAnimation(runnable: Runnable?) {
188         _primaryBouncerDisappearAnimation.value = runnable
189     }
190 
191     override fun setPanelExpansion(panelExpansion: Float) {
192         _panelExpansionAmount.value = panelExpansion
193     }
194 
195     override fun setKeyguardPosition(keyguardPosition: Float) {
196         _keyguardPosition.value = keyguardPosition
197     }
198 
199     override fun setResourceUpdateRequests(willUpdateResources: Boolean) {
200         _resourceUpdateRequests.value = willUpdateResources
201     }
202 
203     override fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
204         _showMessage.value = bouncerShowMessageModel
205     }
206 
207     override fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
208         _keyguardAuthenticated.value = keyguardAuthenticated
209     }
210 
211     override fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) {
212         _isBackButtonEnabled.value = isBackButtonEnabled
213     }
214 
215     override fun setSideFpsShowing(isShowing: Boolean) {
216         _sideFpsShowing.value = isShowing
217     }
218 
219     /** Sets up logs for state flows. */
220     private fun setUpLogging() {
221         if (!Build.IS_DEBUGGABLE) {
222             return
223         }
224 
225         primaryBouncerShow
226             .logDiffsForTable(buffer, "", "PrimaryBouncerShow", false)
227             .onEach { Log.d(TAG, "Keyguard Bouncer is ${if (it) "showing" else "hiding."}") }
228             .launchIn(applicationScope)
229         primaryBouncerShowingSoon
230             .logDiffsForTable(buffer, "", "PrimaryBouncerShowingSoon", false)
231             .launchIn(applicationScope)
232         primaryBouncerStartingToHide
233             .logDiffsForTable(buffer, "", "PrimaryBouncerStartingToHide", false)
234             .launchIn(applicationScope)
235         primaryBouncerStartingDisappearAnimation
236             .map { it != null }
237             .logDiffsForTable(buffer, "", "PrimaryBouncerStartingDisappearAnimation", false)
238             .launchIn(applicationScope)
239         primaryBouncerScrimmed
240             .logDiffsForTable(buffer, "", "PrimaryBouncerScrimmed", false)
241             .launchIn(applicationScope)
242         panelExpansionAmount
243             .map { (it * 1000).toInt() }
244             .logDiffsForTable(buffer, "", "PanelExpansionAmountMillis", -1)
245             .launchIn(applicationScope)
246         keyguardPosition
247             .filterNotNull()
248             .map { it.toInt() }
249             .logDiffsForTable(buffer, "", "KeyguardPosition", -1)
250             .launchIn(applicationScope)
251         isBackButtonEnabled
252             .filterNotNull()
253             .logDiffsForTable(buffer, "", "IsBackButtonEnabled", false)
254             .launchIn(applicationScope)
255         showMessage
256             .map { it?.message }
257             .logDiffsForTable(buffer, "", "ShowMessage", null)
258             .launchIn(applicationScope)
259         resourceUpdateRequests
260             .logDiffsForTable(buffer, "", "ResourceUpdateRequests", false)
261             .launchIn(applicationScope)
262         alternateBouncerUIAvailable
263             .logDiffsForTable(buffer, "", "IsAlternateBouncerUIAvailable", false)
264             .launchIn(applicationScope)
265         sideFpsShowing
266             .logDiffsForTable(buffer, "", "isSideFpsShowing", false)
267             .launchIn(applicationScope)
268     }
269 
270     companion object {
271         private const val NOT_VISIBLE = -1L
272         private const val TAG = "KeyguardBouncerRepositoryImpl"
273     }
274 }
275