• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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