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 androidx.constraintlayout.widget.Barrier
24 import androidx.constraintlayout.widget.ConstraintLayout
25 import androidx.constraintlayout.widget.ConstraintSet
26 import com.android.systemui.dagger.SysUISingleton
27 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
28 import com.android.systemui.keyguard.MigrateClocksToBlueprint
29 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
30 import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor
31 import com.android.systemui.keyguard.shared.model.KeyguardSection
32 import com.android.systemui.keyguard.ui.binder.KeyguardSmartspaceViewBinder
33 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
34 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
35 import com.android.systemui.res.R as R
36 import com.android.systemui.shared.R as sharedR
37 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
38 import dagger.Lazy
39 import javax.inject.Inject
40 
41 @SysUISingleton
42 open class SmartspaceSection
43 @Inject
44 constructor(
45     val context: Context,
46     val keyguardClockViewModel: KeyguardClockViewModel,
47     val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
48     private val keyguardSmartspaceInteractor: KeyguardSmartspaceInteractor,
49     val smartspaceController: LockscreenSmartspaceController,
50     val keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
51     private val blueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
52 ) : KeyguardSection() {
53     private var smartspaceView: View? = null
54     private var weatherView: View? = null
55     private var dateWeatherView: ViewGroup? = null
56 
57     private var smartspaceVisibilityListener: OnGlobalLayoutListener? = null
58     private var pastVisibility: Int = -1
59 
onRebuildBeginnull60     override fun onRebuildBegin() {
61         smartspaceController.suppressDisconnects = true
62     }
63 
onRebuildEndnull64     override fun onRebuildEnd() {
65         smartspaceController.suppressDisconnects = false
66     }
67 
addViewsnull68     override fun addViews(constraintLayout: ConstraintLayout) {
69         if (!MigrateClocksToBlueprint.isEnabled) return
70         if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
71         smartspaceView = smartspaceController.buildAndConnectView(constraintLayout)
72         weatherView = smartspaceController.buildAndConnectWeatherView(constraintLayout)
73         dateWeatherView =
74             smartspaceController.buildAndConnectDateView(constraintLayout) as ViewGroup
75         pastVisibility = smartspaceView?.visibility ?: View.GONE
76         constraintLayout.addView(smartspaceView)
77         if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
78             constraintLayout.addView(dateWeatherView)
79             // Place weather right after the date, before the extras (alarm and dnd)
80             val index = if (dateWeatherView?.childCount == 0) 0 else 1
81             dateWeatherView?.addView(weatherView, index)
82         }
83         keyguardUnlockAnimationController.lockscreenSmartspace = smartspaceView
84         smartspaceVisibilityListener = OnGlobalLayoutListener {
85             smartspaceView?.let {
86                 val newVisibility = it.visibility
87                 if (pastVisibility != newVisibility) {
88                     keyguardSmartspaceInteractor.setBcSmartspaceVisibility(newVisibility)
89                     pastVisibility = newVisibility
90                 }
91             }
92         }
93         smartspaceView?.viewTreeObserver?.addOnGlobalLayoutListener(smartspaceVisibilityListener)
94     }
95 
bindDatanull96     override fun bindData(constraintLayout: ConstraintLayout) {
97         if (!MigrateClocksToBlueprint.isEnabled) return
98         if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
99         KeyguardSmartspaceViewBinder.bind(
100             constraintLayout,
101             keyguardClockViewModel,
102             keyguardSmartspaceViewModel,
103             blueprintInteractor.get(),
104         )
105     }
106 
applyConstraintsnull107     override fun applyConstraints(constraintSet: ConstraintSet) {
108         if (!MigrateClocksToBlueprint.isEnabled) return
109         if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
110         val horizontalPaddingStart = KeyguardSmartspaceViewModel.getSmartspaceStartMargin(context)
111         val horizontalPaddingEnd = KeyguardSmartspaceViewModel.getSmartspaceEndMargin(context)
112         constraintSet.apply {
113             // migrate addDateWeatherView, addWeatherView from KeyguardClockSwitchController
114             constrainHeight(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
115             constrainWidth(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
116             connect(
117                 sharedR.id.date_smartspace_view,
118                 ConstraintSet.START,
119                 ConstraintSet.PARENT_ID,
120                 ConstraintSet.START,
121                 horizontalPaddingStart
122             )
123 
124             // migrate addSmartspaceView from KeyguardClockSwitchController
125             constrainHeight(sharedR.id.bc_smartspace_view, ConstraintSet.WRAP_CONTENT)
126             connect(
127                 sharedR.id.bc_smartspace_view,
128                 ConstraintSet.START,
129                 ConstraintSet.PARENT_ID,
130                 ConstraintSet.START,
131                 horizontalPaddingStart
132             )
133             connect(
134                 sharedR.id.bc_smartspace_view,
135                 ConstraintSet.END,
136                 if (keyguardClockViewModel.clockShouldBeCentered.value) ConstraintSet.PARENT_ID
137                 else R.id.split_shade_guideline,
138                 ConstraintSet.END,
139                 horizontalPaddingEnd
140             )
141 
142             if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) {
143                 clear(sharedR.id.date_smartspace_view, ConstraintSet.TOP)
144                 connect(
145                     sharedR.id.date_smartspace_view,
146                     ConstraintSet.BOTTOM,
147                     sharedR.id.bc_smartspace_view,
148                     ConstraintSet.TOP
149                 )
150             } else {
151                 clear(sharedR.id.date_smartspace_view, ConstraintSet.BOTTOM)
152                 connect(
153                     sharedR.id.date_smartspace_view,
154                     ConstraintSet.TOP,
155                     R.id.lockscreen_clock_view,
156                     ConstraintSet.BOTTOM
157                 )
158                 connect(
159                     sharedR.id.bc_smartspace_view,
160                     ConstraintSet.TOP,
161                     sharedR.id.date_smartspace_view,
162                     ConstraintSet.BOTTOM
163                 )
164             }
165 
166             createBarrier(
167                 R.id.smart_space_barrier_bottom,
168                 Barrier.BOTTOM,
169                 0,
170                 *intArrayOf(
171                     sharedR.id.bc_smartspace_view,
172                     sharedR.id.date_smartspace_view,
173                 )
174             )
175         }
176         updateVisibility(constraintSet)
177     }
178 
removeViewsnull179     override fun removeViews(constraintLayout: ConstraintLayout) {
180         if (!MigrateClocksToBlueprint.isEnabled) return
181         if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
182         listOf(smartspaceView, dateWeatherView).forEach {
183             it?.let {
184                 if (it.parent == constraintLayout) {
185                     constraintLayout.removeView(it)
186                 }
187             }
188         }
189         smartspaceView?.viewTreeObserver?.removeOnGlobalLayoutListener(smartspaceVisibilityListener)
190         smartspaceVisibilityListener = null
191     }
192 
updateVisibilitynull193     private fun updateVisibility(constraintSet: ConstraintSet) {
194         // This may update the visibility of the smartspace views
195         smartspaceController.requestSmartspaceUpdate()
196 
197         constraintSet.apply {
198             val weatherVisibility =
199                 when (keyguardSmartspaceViewModel.isWeatherVisible.value) {
200                     true -> ConstraintSet.VISIBLE
201                     false -> ConstraintSet.GONE
202                 }
203             setVisibility(sharedR.id.weather_smartspace_view, weatherVisibility)
204             setAlpha(
205                 sharedR.id.weather_smartspace_view,
206                 if (weatherVisibility == View.VISIBLE) 1f else 0f
207             )
208             val dateVisibility =
209                 if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) ConstraintSet.GONE
210                 else ConstraintSet.VISIBLE
211             setVisibility(sharedR.id.date_smartspace_view, dateVisibility)
212             setAlpha(
213                 sharedR.id.date_smartspace_view,
214                 if (dateVisibility == ConstraintSet.VISIBLE) 1f else 0f
215             )
216         }
217     }
218 }
219