• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.systemui.keyguard.ui.view.layout.sections
18 
19 import android.content.Context
20 import android.view.View
21 import android.view.ViewGroup
22 import android.view.ViewTreeObserver.OnGlobalLayoutListener
23 import android.widget.LinearLayout
24 import androidx.constraintlayout.widget.Barrier
25 import androidx.constraintlayout.widget.ConstraintLayout
26 import androidx.constraintlayout.widget.ConstraintSet
27 import androidx.constraintlayout.widget.ConstraintSet.GONE
28 import androidx.constraintlayout.widget.ConstraintSet.VISIBLE
29 import com.android.systemui.customization.R as customR
30 import com.android.systemui.dagger.SysUISingleton
31 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
32 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
33 import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor
34 import com.android.systemui.keyguard.shared.model.KeyguardSection
35 import com.android.systemui.keyguard.ui.binder.KeyguardSmartspaceViewBinder
36 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
37 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
38 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
39 import com.android.systemui.res.R as R
40 import com.android.systemui.shade.ShadeDisplayAware
41 import com.android.systemui.shared.R as sharedR
42 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
43 import dagger.Lazy
44 import javax.inject.Inject
45 import kotlinx.coroutines.DisposableHandle
46 
47 @SysUISingleton
48 open class SmartspaceSection
49 @Inject
50 constructor(
51     @ShadeDisplayAware val context: Context,
52     val keyguardClockViewModel: KeyguardClockViewModel,
53     val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
54     private val keyguardSmartspaceInteractor: KeyguardSmartspaceInteractor,
55     val smartspaceController: LockscreenSmartspaceController,
56     val keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
57     private val blueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
58     private val keyguardRootViewModel: KeyguardRootViewModel,
59 ) : KeyguardSection() {
60     private var smartspaceView: View? = null
61     private var dateView: ViewGroup? = null
62     private var dateViewLargeClock: ViewGroup? = null
63 
64     private var smartspaceVisibilityListener: OnGlobalLayoutListener? = null
65     private var pastVisibility: Int = -1
66     private var disposableHandle: DisposableHandle? = null
67 
onRebuildBeginnull68     override fun onRebuildBegin() {
69         smartspaceController.suppressDisconnects = true
70     }
71 
onRebuildEndnull72     override fun onRebuildEnd() {
73         smartspaceController.suppressDisconnects = false
74     }
75 
addViewsnull76     override fun addViews(constraintLayout: ConstraintLayout) {
77         if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
78         smartspaceView = smartspaceController.buildAndConnectView(constraintLayout)
79         dateView =
80             smartspaceController.buildAndConnectDateView(constraintLayout, false) as? ViewGroup
81         var weatherViewLargeClock: View? = null
82         val weatherView: View? =
83             smartspaceController.buildAndConnectWeatherView(constraintLayout, false)
84         if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
85             weatherViewLargeClock =
86                 smartspaceController.buildAndConnectWeatherView(constraintLayout, true)
87             dateViewLargeClock =
88                 smartspaceController.buildAndConnectDateView(constraintLayout, true) as? ViewGroup
89         }
90         pastVisibility = smartspaceView?.visibility ?: View.GONE
91         constraintLayout.addView(smartspaceView)
92         if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
93             dateView?.visibility = View.GONE
94             dateViewLargeClock?.visibility = View.GONE
95             constraintLayout.addView(dateViewLargeClock)
96             if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
97                 // Place weather right after the date, before the extras (alarm and dnd)
98                 val index = if (dateViewLargeClock?.childCount == 0) 0 else 1
99                 dateViewLargeClock?.addView(weatherViewLargeClock, index)
100             }
101 
102             if (
103                 KeyguardSmartspaceViewModel.dateWeatherBelowSmallClock(
104                     context.resources.configuration,
105                     keyguardClockViewModel.hasCustomWeatherDataDisplay.value,
106                 )
107             ) {
108                 (dateView as? LinearLayout)?.orientation = LinearLayout.HORIZONTAL
109             } else {
110                 (dateView as? LinearLayout)?.orientation = LinearLayout.VERTICAL
111             }
112         }
113 
114         if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
115             constraintLayout.addView(dateView)
116             // Place weather right after the date, before the extras (alarm and dnd)
117             val index = if (dateView?.childCount == 0) 0 else 1
118             dateView?.addView(weatherView, index)
119         }
120         keyguardUnlockAnimationController.lockscreenSmartspace = smartspaceView
121         smartspaceVisibilityListener = OnGlobalLayoutListener {
122             smartspaceView?.let {
123                 val newVisibility = it.visibility
124                 if (pastVisibility != newVisibility) {
125                     keyguardSmartspaceInteractor.setBcSmartspaceVisibility(newVisibility)
126                     pastVisibility = newVisibility
127                 }
128             }
129         }
130         smartspaceView?.viewTreeObserver?.addOnGlobalLayoutListener(smartspaceVisibilityListener)
131     }
132 
bindDatanull133     override fun bindData(constraintLayout: ConstraintLayout) {
134         if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
135         disposableHandle?.dispose()
136         disposableHandle =
137             KeyguardSmartspaceViewBinder.bind(
138                 constraintLayout,
139                 keyguardRootViewModel,
140                 keyguardClockViewModel,
141                 keyguardSmartspaceViewModel,
142                 blueprintInteractor.get(),
143             )
144     }
145 
applyConstraintsnull146     override fun applyConstraints(constraintSet: ConstraintSet) {
147         if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
148         val dateWeatherPaddingStart = KeyguardSmartspaceViewModel.getDateWeatherStartMargin(context)
149         val smartspaceHorizontalPadding =
150             KeyguardSmartspaceViewModel.getSmartspaceHorizontalMargin(context)
151         val dateWeatherBelowSmallClock =
152             KeyguardSmartspaceViewModel.dateWeatherBelowSmallClock(
153                 context.resources.configuration,
154                 keyguardClockViewModel.hasCustomWeatherDataDisplay.value,
155             )
156         constraintSet.apply {
157             constrainHeight(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
158             constrainWidth(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
159             if (dateWeatherBelowSmallClock) {
160                 connect(
161                     sharedR.id.date_smartspace_view,
162                     ConstraintSet.START,
163                     ConstraintSet.PARENT_ID,
164                     ConstraintSet.START,
165                     dateWeatherPaddingStart,
166                 )
167             }
168 
169             constrainHeight(sharedR.id.bc_smartspace_view, ConstraintSet.WRAP_CONTENT)
170             constrainWidth(sharedR.id.bc_smartspace_view, ConstraintSet.MATCH_CONSTRAINT)
171             connect(
172                 sharedR.id.bc_smartspace_view,
173                 ConstraintSet.START,
174                 ConstraintSet.PARENT_ID,
175                 ConstraintSet.START,
176                 smartspaceHorizontalPadding,
177             )
178             connect(
179                 sharedR.id.bc_smartspace_view,
180                 ConstraintSet.END,
181                 if (keyguardSmartspaceViewModel.isShadeLayoutWide.value) R.id.split_shade_guideline
182                 else ConstraintSet.PARENT_ID,
183                 ConstraintSet.END,
184                 smartspaceHorizontalPadding,
185             )
186             if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) {
187                 if (dateWeatherBelowSmallClock) {
188                     clear(sharedR.id.date_smartspace_view, ConstraintSet.TOP)
189                     connect(
190                         sharedR.id.date_smartspace_view,
191                         ConstraintSet.BOTTOM,
192                         sharedR.id.bc_smartspace_view,
193                         ConstraintSet.TOP,
194                     )
195                 }
196             } else {
197                 clear(sharedR.id.date_smartspace_view, ConstraintSet.BOTTOM)
198                 if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
199                     if (dateWeatherBelowSmallClock) {
200                         connect(
201                             sharedR.id.date_smartspace_view,
202                             ConstraintSet.TOP,
203                             customR.id.lockscreen_clock_view,
204                             ConstraintSet.BOTTOM,
205                         )
206                         connect(
207                             sharedR.id.bc_smartspace_view,
208                             ConstraintSet.TOP,
209                             sharedR.id.date_smartspace_view,
210                             ConstraintSet.BOTTOM,
211                         )
212                     } else {
213                         connect(
214                             sharedR.id.bc_smartspace_view,
215                             ConstraintSet.TOP,
216                             customR.id.lockscreen_clock_view,
217                             ConstraintSet.BOTTOM,
218                         )
219                     }
220                 } else {
221                     connect(
222                         sharedR.id.date_smartspace_view,
223                         ConstraintSet.TOP,
224                         customR.id.lockscreen_clock_view,
225                         ConstraintSet.BOTTOM,
226                     )
227                     connect(
228                         sharedR.id.bc_smartspace_view,
229                         ConstraintSet.TOP,
230                         sharedR.id.date_smartspace_view,
231                         ConstraintSet.BOTTOM,
232                     )
233                 }
234             }
235 
236             if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
237                 if (keyguardClockViewModel.isLargeClockVisible.value) {
238                     setVisibility(sharedR.id.date_smartspace_view, GONE)
239                     constrainHeight(
240                         sharedR.id.date_smartspace_view_large,
241                         ConstraintSet.WRAP_CONTENT,
242                     )
243                     constrainWidth(
244                         sharedR.id.date_smartspace_view_large,
245                         ConstraintSet.WRAP_CONTENT,
246                     )
247                     constrainHeight(
248                         sharedR.id.weather_smartspace_view_large,
249                         ConstraintSet.WRAP_CONTENT,
250                     )
251                     constrainWidth(
252                         sharedR.id.weather_smartspace_view_large,
253                         ConstraintSet.WRAP_CONTENT,
254                     )
255                     connect(
256                         sharedR.id.date_smartspace_view_large,
257                         ConstraintSet.TOP,
258                         customR.id.lockscreen_clock_view_large,
259                         ConstraintSet.BOTTOM,
260                         context.resources.getDimensionPixelSize(R.dimen.smartspace_padding_vertical),
261                     )
262 
263                     connect(
264                         sharedR.id.date_smartspace_view_large,
265                         ConstraintSet.START,
266                         customR.id.lockscreen_clock_view_large,
267                         ConstraintSet.START,
268                     )
269                     connect(
270                         sharedR.id.date_smartspace_view_large,
271                         ConstraintSet.END,
272                         customR.id.lockscreen_clock_view_large,
273                         ConstraintSet.END,
274                     )
275                     setHorizontalChainStyle(
276                         sharedR.id.date_smartspace_view_large,
277                         ConstraintSet.CHAIN_PACKED,
278                     )
279                 } else {
280                     if (dateWeatherBelowSmallClock) {
281                         connect(
282                             sharedR.id.date_smartspace_view,
283                             ConstraintSet.START,
284                             ConstraintSet.PARENT_ID,
285                             ConstraintSet.START,
286                             dateWeatherPaddingStart,
287                         )
288                     } else {
289                         setVisibility(sharedR.id.date_smartspace_view_large, GONE)
290                         constrainHeight(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
291                         constrainWidth(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
292                         connect(
293                             sharedR.id.date_smartspace_view,
294                             ConstraintSet.START,
295                             customR.id.lockscreen_clock_view,
296                             ConstraintSet.END,
297                             context.resources.getDimensionPixelSize(
298                                 R.dimen.smartspace_padding_horizontal
299                             ),
300                         )
301                         connect(
302                             sharedR.id.date_smartspace_view,
303                             ConstraintSet.TOP,
304                             customR.id.lockscreen_clock_view,
305                             ConstraintSet.TOP,
306                         )
307                         connect(
308                             sharedR.id.date_smartspace_view,
309                             ConstraintSet.BOTTOM,
310                             customR.id.lockscreen_clock_view,
311                             ConstraintSet.BOTTOM,
312                         )
313                     }
314                 }
315             }
316 
317             if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
318                 if (dateWeatherBelowSmallClock) {
319                     createBarrier(
320                         R.id.smart_space_barrier_bottom,
321                         Barrier.BOTTOM,
322                         0,
323                         *intArrayOf(sharedR.id.bc_smartspace_view, sharedR.id.date_smartspace_view),
324                     )
325                     createBarrier(
326                         R.id.smart_space_barrier_top,
327                         Barrier.TOP,
328                         0,
329                         *intArrayOf(sharedR.id.bc_smartspace_view, sharedR.id.date_smartspace_view),
330                     )
331                 } else {
332                     createBarrier(
333                         R.id.smart_space_barrier_bottom,
334                         Barrier.BOTTOM,
335                         0,
336                         sharedR.id.bc_smartspace_view,
337                     )
338                     createBarrier(
339                         R.id.smart_space_barrier_top,
340                         Barrier.TOP,
341                         0,
342                         sharedR.id.bc_smartspace_view,
343                     )
344                 }
345             } else {
346                 createBarrier(
347                     R.id.smart_space_barrier_bottom,
348                     Barrier.BOTTOM,
349                     0,
350                     *intArrayOf(sharedR.id.bc_smartspace_view, sharedR.id.date_smartspace_view),
351                 )
352                 createBarrier(
353                     R.id.smart_space_barrier_top,
354                     Barrier.TOP,
355                     0,
356                     *intArrayOf(sharedR.id.bc_smartspace_view, sharedR.id.date_smartspace_view),
357                 )
358             }
359         }
360         updateVisibility(constraintSet)
361     }
362 
removeViewsnull363     override fun removeViews(constraintLayout: ConstraintLayout) {
364         if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
365 
366         val list =
367             if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
368                 listOf(smartspaceView, dateView, dateViewLargeClock)
369             } else {
370                 listOf(smartspaceView, dateView)
371             }
372         list.forEach {
373             it?.let {
374                 if (it.parent == constraintLayout) {
375                     constraintLayout.removeView(it)
376                 }
377             }
378         }
379         smartspaceView?.viewTreeObserver?.removeOnGlobalLayoutListener(smartspaceVisibilityListener)
380         smartspaceVisibilityListener = null
381 
382         disposableHandle?.dispose()
383     }
384 
updateVisibilitynull385     private fun updateVisibility(constraintSet: ConstraintSet) {
386 
387         // This may update the visibility of the smartspace views
388         smartspaceController.requestSmartspaceUpdate()
389         val weatherId: Int
390         val dateId: Int
391         if (
392             com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout() &&
393                 keyguardClockViewModel.isLargeClockVisible.value
394         ) {
395             weatherId = sharedR.id.weather_smartspace_view_large
396             dateId = sharedR.id.date_smartspace_view_large
397         } else {
398             weatherId = sharedR.id.weather_smartspace_view
399             dateId = sharedR.id.date_smartspace_view
400         }
401 
402         constraintSet.apply {
403             val showWeather = keyguardSmartspaceViewModel.isWeatherVisible.value
404             setVisibility(weatherId, if (showWeather) VISIBLE else GONE)
405             setAlpha(weatherId, if (showWeather) 1f else 0f)
406 
407             val showDateView = !keyguardClockViewModel.hasCustomWeatherDataDisplay.value
408             setVisibility(dateId, if (showDateView) VISIBLE else GONE)
409             setAlpha(dateId, if (showDateView) 1f else 0f)
410 
411             if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
412                 if (keyguardClockViewModel.isLargeClockVisible.value) {
413                     setVisibility(sharedR.id.date_smartspace_view, GONE)
414                 } else {
415                     setVisibility(sharedR.id.date_smartspace_view_large, GONE)
416                 }
417             }
418         }
419     }
420 }
421