1 /*
<lambda>null2 * 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.ui.view.layout.sections
19
20 import android.content.Context
21 import android.view.View
22 import androidx.constraintlayout.widget.Barrier
23 import androidx.constraintlayout.widget.ConstraintLayout
24 import androidx.constraintlayout.widget.ConstraintSet
25 import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
26 import androidx.constraintlayout.widget.ConstraintSet.END
27 import androidx.constraintlayout.widget.ConstraintSet.GONE
28 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
29 import androidx.constraintlayout.widget.ConstraintSet.START
30 import androidx.constraintlayout.widget.ConstraintSet.TOP
31 import androidx.constraintlayout.widget.ConstraintSet.VISIBLE
32 import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
33 import com.android.systemui.customization.R as customR
34 import com.android.systemui.dagger.SysUISingleton
35 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
36 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
37 import com.android.systemui.keyguard.shared.model.KeyguardSection
38 import com.android.systemui.keyguard.ui.binder.KeyguardClockViewBinder
39 import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
40 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
41 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
42 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
43 import com.android.systemui.plugins.clocks.ClockController
44 import com.android.systemui.plugins.clocks.ClockFaceLayout
45 import com.android.systemui.res.R
46 import com.android.systemui.shade.LargeScreenHeaderHelper
47 import com.android.systemui.shade.ShadeDisplayAware
48 import com.android.systemui.shared.R as sharedR
49 import com.android.systemui.util.ui.value
50 import dagger.Lazy
51 import javax.inject.Inject
52 import kotlinx.coroutines.DisposableHandle
53
54 internal fun ConstraintSet.setVisibility(views: Iterable<View>, visibility: Int) =
55 views.forEach { view -> this.setVisibility(view.id, visibility) }
56
setAlphanull57 private fun ConstraintSet.setAlpha(views: Iterable<View>, alpha: Float) =
58 views.forEach { view -> this.setAlpha(view.id, alpha) }
59
setScaleXnull60 private fun ConstraintSet.setScaleX(views: Iterable<View>, scaleX: Float) =
61 views.forEach { view -> this.setScaleX(view.id, scaleX) }
62
setScaleYnull63 private fun ConstraintSet.setScaleY(views: Iterable<View>, scaleY: Float) =
64 views.forEach { view -> this.setScaleY(view.id, scaleY) }
65
66 @SysUISingleton
67 class ClockSection
68 @Inject
69 constructor(
70 private val clockInteractor: KeyguardClockInteractor,
71 protected val keyguardClockViewModel: KeyguardClockViewModel,
72 @ShadeDisplayAware private val context: Context,
73 val smartspaceViewModel: KeyguardSmartspaceViewModel,
74 val blueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
75 private val rootViewModel: KeyguardRootViewModel,
76 private val aodBurnInViewModel: AodBurnInViewModel,
77 private val largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>,
78 ) : KeyguardSection() {
79 private var disposableHandle: DisposableHandle? = null
80
addViewsnull81 override fun addViews(constraintLayout: ConstraintLayout) {}
82
bindDatanull83 override fun bindData(constraintLayout: ConstraintLayout) {
84 disposableHandle?.dispose()
85 disposableHandle =
86 KeyguardClockViewBinder.bind(
87 this,
88 constraintLayout,
89 keyguardClockViewModel,
90 clockInteractor,
91 blueprintInteractor.get(),
92 rootViewModel,
93 aodBurnInViewModel,
94 )
95 }
96
applyConstraintsnull97 override fun applyConstraints(constraintSet: ConstraintSet) {
98 keyguardClockViewModel.currentClock.value?.let { clock ->
99 constraintSet.applyDeltaFrom(buildConstraints(clock, constraintSet))
100 }
101 }
102
removeViewsnull103 override fun removeViews(constraintLayout: ConstraintLayout) {
104 disposableHandle?.dispose()
105 }
106
buildConstraintsnull107 private fun buildConstraints(
108 clock: ClockController,
109 constraintSet: ConstraintSet,
110 ): ConstraintSet {
111 // Add constraint between rootView and clockContainer
112 applyDefaultConstraints(constraintSet)
113 getNonTargetClockFace(clock).applyConstraints(constraintSet)
114 getTargetClockFace(clock).applyConstraints(constraintSet)
115
116 // Add constraint between elements in clock and clock container
117 return constraintSet.apply {
118 setVisibility(getTargetClockFace(clock).views, VISIBLE)
119 setVisibility(getNonTargetClockFace(clock).views, GONE)
120 setAlpha(getTargetClockFace(clock).views, 1F)
121 setAlpha(getNonTargetClockFace(clock).views, 0F)
122
123 if (!keyguardClockViewModel.isLargeClockVisible.value) {
124 if (
125 KeyguardSmartspaceViewModel.dateWeatherBelowSmallClock(
126 context.resources.configuration
127 )
128 ) {
129 connect(
130 sharedR.id.bc_smartspace_view,
131 TOP,
132 sharedR.id.date_smartspace_view,
133 BOTTOM,
134 )
135 } else {
136 connect(
137 sharedR.id.bc_smartspace_view,
138 TOP,
139 customR.id.lockscreen_clock_view,
140 BOTTOM,
141 )
142 }
143 } else {
144 setScaleX(getTargetClockFace(clock).views, aodBurnInViewModel.movement.value.scale)
145 setScaleY(getTargetClockFace(clock).views, aodBurnInViewModel.movement.value.scale)
146 }
147 }
148 }
149
getTargetClockFacenull150 private fun getTargetClockFace(clock: ClockController): ClockFaceLayout =
151 if (keyguardClockViewModel.isLargeClockVisible.value) clock.largeClock.layout
152 else clock.smallClock.layout
153
154 private fun getNonTargetClockFace(clock: ClockController): ClockFaceLayout =
155 if (keyguardClockViewModel.isLargeClockVisible.value) clock.smallClock.layout
156 else clock.largeClock.layout
157
158 private fun constrainWeatherClockDateIconsBarrier(constraints: ConstraintSet) {
159 constraints.apply {
160 createBarrier(
161 R.id.weather_clock_bc_smartspace_bottom,
162 Barrier.BOTTOM,
163 getDimen(ENHANCED_SMARTSPACE_HEIGHT),
164 (customR.id.weather_clock_time),
165 )
166 if (
167 rootViewModel.isNotifIconContainerVisible.value.value &&
168 keyguardClockViewModel.hasAodIcons.value
169 ) {
170 createBarrier(
171 R.id.weather_clock_date_and_icons_barrier_bottom,
172 Barrier.BOTTOM,
173 0,
174 *intArrayOf(
175 R.id.aod_notification_icon_container,
176 R.id.weather_clock_bc_smartspace_bottom,
177 ),
178 )
179 } else {
180 createBarrier(
181 R.id.weather_clock_date_and_icons_barrier_bottom,
182 Barrier.BOTTOM,
183 0,
184 *intArrayOf(R.id.weather_clock_bc_smartspace_bottom),
185 )
186 }
187 }
188 }
189
applyDefaultConstraintsnull190 fun applyDefaultConstraints(constraints: ConstraintSet) {
191 val guideline =
192 if (keyguardClockViewModel.clockShouldBeCentered.value) PARENT_ID
193 else R.id.split_shade_guideline
194 val dateWeatherBelowSmallClock =
195 KeyguardSmartspaceViewModel.dateWeatherBelowSmallClock(context.resources.configuration)
196 constraints.apply {
197 connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START)
198 connect(customR.id.lockscreen_clock_view_large, END, guideline, END)
199 connect(
200 customR.id.lockscreen_clock_view_large,
201 BOTTOM,
202 R.id.device_entry_icon_view,
203 TOP,
204 )
205 val largeClockTopMargin =
206 if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
207 keyguardClockViewModel.getLargeClockTopMargin() +
208 getDimen(ENHANCED_SMARTSPACE_HEIGHT)
209 } else {
210 keyguardClockViewModel.getLargeClockTopMargin() +
211 getDimen(DATE_WEATHER_VIEW_HEIGHT) +
212 getDimen(ENHANCED_SMARTSPACE_HEIGHT)
213 }
214 connect(
215 customR.id.lockscreen_clock_view_large,
216 TOP,
217 PARENT_ID,
218 TOP,
219 largeClockTopMargin,
220 )
221 constrainWidth(customR.id.lockscreen_clock_view_large, WRAP_CONTENT)
222
223 // The following two lines make lockscreen_clock_view_large is constrained to available
224 // height when it goes beyond constraints; otherwise, it use WRAP_CONTENT
225 constrainHeight(customR.id.lockscreen_clock_view_large, WRAP_CONTENT)
226 constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0)
227 constrainWidth(customR.id.lockscreen_clock_view, WRAP_CONTENT)
228 constrainHeight(
229 customR.id.lockscreen_clock_view,
230 context.resources.getDimensionPixelSize(customR.dimen.small_clock_height),
231 )
232 connect(
233 customR.id.lockscreen_clock_view,
234 START,
235 PARENT_ID,
236 START,
237 context.resources.getDimensionPixelSize(customR.dimen.clock_padding_start) +
238 context.resources.getDimensionPixelSize(
239 customR.dimen.status_view_margin_horizontal
240 ),
241 )
242 val smallClockTopMargin = keyguardClockViewModel.getSmallClockTopMargin()
243 create(R.id.small_clock_guideline_top, ConstraintSet.HORIZONTAL_GUIDELINE)
244 setGuidelineBegin(R.id.small_clock_guideline_top, smallClockTopMargin)
245 connect(customR.id.lockscreen_clock_view, TOP, R.id.small_clock_guideline_top, BOTTOM)
246
247 // Explicitly clear pivot to force recalculate pivot instead of using legacy value
248 setTransformPivot(customR.id.lockscreen_clock_view_large, Float.NaN, Float.NaN)
249
250 val smallClockBottom =
251 keyguardClockViewModel.getSmallClockTopMargin() +
252 context.resources.getDimensionPixelSize(customR.dimen.small_clock_height)
253 val marginBetweenSmartspaceAndNotification =
254 context.resources.getDimensionPixelSize(
255 R.dimen.keyguard_status_view_bottom_margin
256 ) +
257 if (context.resources.getBoolean(R.bool.config_use_large_screen_shade_header)) {
258 largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
259 } else {
260 0
261 }
262
263 if (dateWeatherBelowSmallClock) {
264 val dateWeatherSmartspaceHeight =
265 getDimen(context, DATE_WEATHER_VIEW_HEIGHT).toFloat()
266 clockInteractor.setNotificationStackDefaultTop(
267 smallClockBottom +
268 dateWeatherSmartspaceHeight +
269 marginBetweenSmartspaceAndNotification
270 )
271 } else {
272 clockInteractor.setNotificationStackDefaultTop(
273 (smallClockBottom + marginBetweenSmartspaceAndNotification).toFloat()
274 )
275 }
276 }
277
278 constrainWeatherClockDateIconsBarrier(constraints)
279 }
280
getDimennull281 private fun getDimen(name: String): Int {
282 return getDimen(context, name)
283 }
284
285 companion object {
286 private const val DATE_WEATHER_VIEW_HEIGHT = "date_weather_view_height"
287 private const val ENHANCED_SMARTSPACE_HEIGHT = "enhanced_smartspace_height"
288
getDimennull289 fun getDimen(context: Context, name: String): Int {
290 val res = context.packageManager.getResourcesForApplication(context.packageName)
291 val id = res.getIdentifier(name, "dimen", context.packageName)
292 return if (id == 0) 0 else res.getDimensionPixelSize(id)
293 }
294 }
295 }
296