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