1 /* 2 * Copyright (C) 2023 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 18 package com.android.systemui.keyguard 19 20 import android.content.Context 21 import android.view.LayoutInflater 22 import android.view.View 23 import androidx.compose.foundation.layout.fillMaxSize 24 import androidx.compose.ui.Modifier 25 import androidx.compose.ui.platform.ComposeView 26 import androidx.constraintlayout.widget.ConstraintSet 27 import androidx.constraintlayout.widget.ConstraintSet.BOTTOM 28 import androidx.constraintlayout.widget.ConstraintSet.END 29 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID 30 import androidx.constraintlayout.widget.ConstraintSet.START 31 import androidx.constraintlayout.widget.ConstraintSet.TOP 32 import com.android.compose.animation.scene.SceneKey 33 import com.android.compose.animation.scene.SceneTransitionLayout 34 import com.android.compose.animation.scene.transitions 35 import com.android.internal.jank.InteractionJankMonitor 36 import com.android.keyguard.KeyguardStatusView 37 import com.android.keyguard.KeyguardStatusViewController 38 import com.android.keyguard.LegacyLockIconViewController 39 import com.android.keyguard.LockIconView 40 import com.android.keyguard.dagger.KeyguardStatusViewComponent 41 import com.android.systemui.CoreStartable 42 import com.android.systemui.biometrics.ui.binder.DeviceEntryUnlockTrackerViewBinder 43 import com.android.systemui.common.ui.ConfigurationState 44 import com.android.systemui.dagger.SysUISingleton 45 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor 46 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor 47 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor 48 import com.android.systemui.keyguard.shared.ComposeLockscreen 49 import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint 50 import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder 51 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder 52 import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder 53 import com.android.systemui.keyguard.ui.composable.LockscreenContent 54 import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint 55 import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea 56 import com.android.systemui.keyguard.ui.view.KeyguardRootView 57 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel 58 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel 59 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel 60 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel 61 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel 62 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel 63 import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel 64 import com.android.systemui.plugins.FalsingManager 65 import com.android.systemui.res.R 66 import com.android.systemui.scene.shared.flag.SceneContainerFlag 67 import com.android.systemui.shade.NotificationShadeWindowView 68 import com.android.systemui.shade.domain.interactor.ShadeInteractor 69 import com.android.systemui.statusbar.KeyguardIndicationController 70 import com.android.systemui.statusbar.VibratorHelper 71 import com.android.systemui.statusbar.phone.ScreenOffAnimationController 72 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator 73 import dagger.Lazy 74 import java.util.Optional 75 import javax.inject.Inject 76 import kotlinx.coroutines.DisposableHandle 77 import kotlinx.coroutines.ExperimentalCoroutinesApi 78 79 /** Binds keyguard views on startup, and also exposes methods to allow rebinding if views change */ 80 @ExperimentalCoroutinesApi 81 @SysUISingleton 82 class KeyguardViewConfigurator 83 @Inject 84 constructor( 85 private val keyguardRootView: KeyguardRootView, 86 private val keyguardRootViewModel: KeyguardRootViewModel, 87 private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel, 88 private val notificationShadeWindowView: NotificationShadeWindowView, 89 private val indicationController: KeyguardIndicationController, 90 private val screenOffAnimationController: ScreenOffAnimationController, 91 private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel, 92 private val chipbarCoordinator: ChipbarCoordinator, 93 private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel, 94 private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory, 95 private val configuration: ConfigurationState, 96 private val context: Context, 97 private val keyguardIndicationController: KeyguardIndicationController, 98 private val lockIconViewController: Lazy<LegacyLockIconViewController>, 99 private val shadeInteractor: ShadeInteractor, 100 private val interactionJankMonitor: InteractionJankMonitor, 101 private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor, 102 private val vibratorHelper: VibratorHelper, 103 private val falsingManager: FalsingManager, 104 private val keyguardClockViewModel: KeyguardClockViewModel, 105 private val smartspaceViewModel: KeyguardSmartspaceViewModel, 106 private val lockscreenContentViewModel: LockscreenContentViewModel, 107 private val lockscreenSceneBlueprintsLazy: Lazy<Set<LockscreenSceneBlueprint>>, 108 private val clockInteractor: KeyguardClockInteractor, 109 private val keyguardViewMediator: KeyguardViewMediator, 110 private val deviceEntryUnlockTrackerViewBinder: Optional<DeviceEntryUnlockTrackerViewBinder>, 111 ) : CoreStartable { 112 113 private var rootViewHandle: DisposableHandle? = null 114 private var indicationAreaHandle: DisposableHandle? = null 115 private val sceneKey = SceneKey("root-view-scene-key") 116 117 var keyguardStatusViewController: KeyguardStatusViewController? = null 118 get() { 119 if (field == null) { 120 val statusViewComponent = 121 keyguardStatusViewComponentFactory.build( 122 LayoutInflater.from(context).inflate(R.layout.keyguard_status_view, null) 123 as KeyguardStatusView, 124 context.display 125 ) 126 val controller = statusViewComponent.keyguardStatusViewController 127 controller.init() 128 field = controller 129 } 130 131 return field 132 } 133 startnull134 override fun start() { 135 bindKeyguardRootView() 136 initializeViews() 137 138 if (!SceneContainerFlag.isEnabled) { 139 if (ComposeLockscreen.isEnabled) { 140 val composeView = 141 createLockscreen( 142 context = context, 143 viewModel = lockscreenContentViewModel, 144 blueprints = lockscreenSceneBlueprintsLazy.get(), 145 ) 146 composeView.id = View.generateViewId() 147 val cs = ConstraintSet() 148 cs.clone(keyguardRootView) 149 cs.connect(composeView.id, START, PARENT_ID, START) 150 cs.connect(composeView.id, END, PARENT_ID, END) 151 cs.connect(composeView.id, TOP, PARENT_ID, TOP) 152 cs.connect(composeView.id, BOTTOM, PARENT_ID, BOTTOM) 153 keyguardRootView.addView(composeView) 154 } else { 155 KeyguardBlueprintViewBinder.bind( 156 keyguardRootView, 157 keyguardBlueprintViewModel, 158 keyguardClockViewModel, 159 smartspaceViewModel, 160 ) 161 } 162 } 163 if (deviceEntryUnlockTrackerViewBinder.isPresent) { 164 deviceEntryUnlockTrackerViewBinder.get().bind(keyguardRootView) 165 } 166 } 167 bindIndicationAreanull168 fun bindIndicationArea() { 169 indicationAreaHandle?.dispose() 170 171 if (!KeyguardBottomAreaRefactor.isEnabled) { 172 keyguardRootView.findViewById<View?>(R.id.keyguard_indication_area)?.let { 173 keyguardRootView.removeView(it) 174 } 175 } 176 177 indicationAreaHandle = 178 KeyguardIndicationAreaBinder.bind( 179 notificationShadeWindowView.requireViewById(R.id.keyguard_indication_area), 180 keyguardIndicationAreaViewModel, 181 indicationController, 182 ) 183 } 184 185 /** Initialize views so that corresponding controllers have a view set. */ initializeViewsnull186 private fun initializeViews() { 187 val indicationArea = KeyguardIndicationArea(context, null) 188 keyguardIndicationController.setIndicationArea(indicationArea) 189 190 if (!DeviceEntryUdfpsRefactor.isEnabled) { 191 lockIconViewController.get().setLockIconView(LockIconView(context, null)) 192 } 193 } 194 bindKeyguardRootViewnull195 private fun bindKeyguardRootView() { 196 if (SceneContainerFlag.isEnabled) { 197 return 198 } 199 200 rootViewHandle?.dispose() 201 rootViewHandle = 202 KeyguardRootViewBinder.bind( 203 keyguardRootView, 204 keyguardRootViewModel, 205 keyguardBlueprintViewModel, 206 configuration, 207 occludingAppDeviceEntryMessageViewModel, 208 chipbarCoordinator, 209 screenOffAnimationController, 210 shadeInteractor, 211 clockInteractor, 212 keyguardClockViewModel, 213 interactionJankMonitor, 214 deviceEntryHapticsInteractor, 215 vibratorHelper, 216 falsingManager, 217 keyguardViewMediator, 218 ) 219 } 220 createLockscreennull221 private fun createLockscreen( 222 context: Context, 223 viewModel: LockscreenContentViewModel, 224 blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>, 225 ): View { 226 val sceneBlueprints = 227 blueprints.mapNotNull { it as? ComposableLockscreenSceneBlueprint }.toSet() 228 return ComposeView(context).apply { 229 setContent { 230 // STL is used solely to provide a SceneScope to enable us to invoke SceneScope 231 // composables. 232 SceneTransitionLayout( 233 currentScene = sceneKey, 234 onChangeScene = {}, 235 transitions = transitions {}, 236 ) { 237 scene(sceneKey) { 238 with( 239 LockscreenContent( 240 viewModel = viewModel, 241 blueprints = sceneBlueprints, 242 clockInteractor = clockInteractor 243 ) 244 ) { 245 Content(modifier = Modifier.fillMaxSize()) 246 } 247 } 248 } 249 } 250 } 251 } 252 253 /** 254 * Temporary, to allow NotificationPanelViewController to use the same instance while code is 255 * migrated: b/288242803 256 */ getKeyguardRootViewnull257 fun getKeyguardRootView() = keyguardRootView 258 } 259