1 /*
<lambda>null2  * 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.ui.binder
18 
19 import android.view.KeyEvent
20 import android.view.View
21 import android.view.ViewGroup
22 import android.window.OnBackAnimationCallback
23 import androidx.lifecycle.Lifecycle
24 import androidx.lifecycle.repeatOnLifecycle
25 import com.android.keyguard.KeyguardMessageAreaController
26 import com.android.keyguard.KeyguardSecurityContainerController
27 import com.android.keyguard.KeyguardSecurityModel
28 import com.android.keyguard.KeyguardSecurityView
29 import com.android.keyguard.dagger.KeyguardBouncerComponent
30 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
31 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
32 import com.android.systemui.bouncer.ui.BouncerViewDelegate
33 import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
34 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
35 import com.android.systemui.lifecycle.repeatWhenAttached
36 import com.android.systemui.log.BouncerLogger
37 import com.android.systemui.plugins.ActivityStarter
38 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
39 import kotlinx.coroutines.awaitCancellation
40 import kotlinx.coroutines.flow.filter
41 import kotlinx.coroutines.launch
42 
43 /** Binds the bouncer container to its view model. */
44 object KeyguardBouncerViewBinder {
45     @JvmStatic
46     fun bind(
47         view: ViewGroup,
48         viewModel: KeyguardBouncerViewModel,
49         primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel,
50         componentFactory: KeyguardBouncerComponent.Factory,
51         messageAreaControllerFactory: KeyguardMessageAreaController.Factory,
52         bouncerMessageInteractor: BouncerMessageInteractor,
53         bouncerLogger: BouncerLogger,
54         selectedUserInteractor: SelectedUserInteractor,
55     ) {
56         // Builds the KeyguardSecurityContainerController from bouncer view group.
57         val securityContainerController: KeyguardSecurityContainerController =
58             componentFactory.create(view).securityContainerController
59         securityContainerController.init()
60         val delegate =
61             object : BouncerViewDelegate {
62                 override fun isFullScreenBouncer(): Boolean {
63                     val mode = securityContainerController.currentSecurityMode
64                     return mode == KeyguardSecurityModel.SecurityMode.SimPin ||
65                         mode == KeyguardSecurityModel.SecurityMode.SimPuk
66                 }
67 
68                 override fun getBackCallback(): OnBackAnimationCallback {
69                     return securityContainerController.backCallback
70                 }
71 
72                 override fun shouldDismissOnMenuPressed(): Boolean {
73                     return securityContainerController.shouldEnableMenuKey()
74                 }
75 
76                 override fun interceptMediaKey(event: KeyEvent?): Boolean {
77                     return securityContainerController.interceptMediaKey(event)
78                 }
79 
80                 override fun dispatchBackKeyEventPreIme(): Boolean {
81                     return securityContainerController.dispatchBackKeyEventPreIme()
82                 }
83 
84                 override fun showNextSecurityScreenOrFinish(): Boolean {
85                     return securityContainerController.dismiss(
86                         selectedUserInteractor.getSelectedUserId()
87                     )
88                 }
89 
90                 override fun resume() {
91                     securityContainerController.showPrimarySecurityScreen(/* isTurningOff= */ false)
92                     securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON)
93                 }
94 
95                 override fun setDismissAction(
96                     onDismissAction: ActivityStarter.OnDismissAction?,
97                     cancelAction: Runnable?
98                 ) {
99                     securityContainerController.setOnDismissAction(onDismissAction, cancelAction)
100                 }
101 
102                 override fun willDismissWithActions(): Boolean {
103                     return securityContainerController.hasDismissActions()
104                 }
105 
106                 override fun willRunDismissFromKeyguard(): Boolean {
107                     return securityContainerController.willRunDismissFromKeyguard()
108                 }
109 
110                 override fun showPromptReason(reason: Int) {
111                     securityContainerController.showPromptReason(reason)
112                 }
113             }
114         view.repeatWhenAttached {
115             repeatOnLifecycle(Lifecycle.State.CREATED) {
116                 try {
117                     viewModel.setBouncerViewDelegate(delegate)
118                     launch {
119                         viewModel.isShowing.collect { isShowing ->
120                             view.visibility = if (isShowing) View.VISIBLE else View.INVISIBLE
121                             if (isShowing) {
122                                 // Reset security container because these views are not reinflated.
123                                 securityContainerController.prepareToShow()
124                                 securityContainerController.reinflateViewFlipper {
125                                     // Reset Security Container entirely.
126                                     securityContainerController.onBouncerVisibilityChanged(
127                                         /* isVisible= */ true
128                                     )
129                                     securityContainerController.showPrimarySecurityScreen(
130                                         /* turningOff= */ false
131                                     )
132                                     securityContainerController.setInitialMessage()
133                                     securityContainerController.appear()
134                                     securityContainerController.onResume(
135                                         KeyguardSecurityView.SCREEN_ON
136                                     )
137                                     bouncerLogger.bindingBouncerMessageView()
138                                     it.bindMessageView(
139                                         bouncerMessageInteractor,
140                                         messageAreaControllerFactory,
141                                         bouncerLogger
142                                     )
143                                 }
144                             } else {
145                                 securityContainerController.onBouncerVisibilityChanged(
146                                     /* isVisible= */ false
147                                 )
148                                 securityContainerController.cancelDismissAction()
149                                 securityContainerController.reset()
150                                 securityContainerController.onPause()
151                             }
152                         }
153                     }
154 
155                     launch {
156                         viewModel.startingToHide.collect {
157                             securityContainerController.onStartingToHide()
158                         }
159                     }
160 
161                     launch {
162                         viewModel.startDisappearAnimation.collect {
163                             securityContainerController.startDisappearAnimation(it)
164                         }
165                     }
166 
167                     launch {
168                         viewModel.bouncerExpansionAmount.collect { expansion ->
169                             securityContainerController.setExpansion(expansion)
170                         }
171                     }
172 
173                     launch {
174                         primaryBouncerToGoneTransitionViewModel.bouncerAlpha.collect { alpha ->
175                             securityContainerController.setAlpha(alpha)
176                         }
177                     }
178 
179                     launch {
180                         viewModel.bouncerExpansionAmount
181                             .filter { it == EXPANSION_VISIBLE }
182                             .collect {
183                                 securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON)
184                                 view.announceForAccessibility(securityContainerController.title)
185                             }
186                     }
187 
188                     launch {
189                         viewModel.isInteractable.collect { isInteractable ->
190                             securityContainerController.setInteractable(isInteractable)
191                         }
192                     }
193 
194                     launch {
195                         viewModel.keyguardPosition.collect { position ->
196                             securityContainerController.updateKeyguardPosition(position)
197                         }
198                     }
199 
200                     launch {
201                         viewModel.updateResources.collect {
202                             securityContainerController.updateResources()
203                             viewModel.notifyUpdateResources()
204                         }
205                     }
206 
207                     launch {
208                         viewModel.bouncerShowMessage.collect {
209                             securityContainerController.showMessage(
210                                 it.message,
211                                 it.colorStateList,
212                                 /* animated= */ true
213                             )
214                             viewModel.onMessageShown()
215                         }
216                     }
217 
218                     launch {
219                         viewModel.keyguardAuthenticated.collect {
220                             securityContainerController.finish(
221                                 selectedUserInteractor.getSelectedUserId()
222                             )
223                             viewModel.notifyKeyguardAuthenticated()
224                         }
225                     }
226 
227                     launch {
228                         viewModel
229                             .observeOnIsBackButtonEnabled { view.systemUiVisibility }
230                             .collect { view.systemUiVisibility = it }
231                     }
232 
233                     awaitCancellation()
234                 } finally {
235                     viewModel.setBouncerViewDelegate(null)
236                 }
237             }
238         }
239     }
240 }
241