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