• 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 package com.android.systemui.keyguard.ui.composable.section
18 
19 import android.content.res.Resources
20 import android.view.View
21 import android.view.ViewGroup
22 import android.widget.FrameLayout
23 import androidx.compose.foundation.layout.fillMaxSize
24 import androidx.compose.foundation.layout.height
25 import androidx.compose.foundation.layout.padding
26 import androidx.compose.runtime.Composable
27 import androidx.compose.runtime.LaunchedEffect
28 import androidx.compose.runtime.getValue
29 import androidx.compose.ui.Modifier
30 import androidx.compose.ui.res.dimensionResource
31 import androidx.compose.ui.viewinterop.AndroidView
32 import androidx.lifecycle.compose.collectAsStateWithLifecycle
33 import com.android.compose.animation.scene.ContentScope
34 import com.android.compose.modifiers.padding
35 import com.android.systemui.customization.R
36 import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.largeClockElementKey
37 import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smallClockElementKey
38 import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
39 import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitShadeLargeClockScene
40 import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
41 import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
42 import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
43 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
44 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
45 import javax.inject.Inject
46 
47 @Composable
48 fun ClockView(view: View?, modifier: Modifier = Modifier) {
49     AndroidView(
50         factory = {
51             FrameLayout(it).apply {
52                 // Clip nothing. The clock views at times render outside their bounds. Compose does
53                 // not clip by default, so only this layer needs clipping to be explicitly disabled.
54                 clipChildren = false
55                 clipToPadding = false
56             }
57         },
58         update = { parent ->
59             view?.let {
60                 parent.removeAllViews()
61                 (view.parent as? ViewGroup)?.removeView(view)
62                 parent.addView(view)
63             } ?: run { parent.removeAllViews() }
64         },
65         modifier = modifier,
66     )
67 }
68 
69 /** Provides small clock and large clock composables for the default clock face. */
70 class DefaultClockSection
71 @Inject
72 constructor(
73     private val viewModel: KeyguardClockViewModel,
74     private val aodBurnInViewModel: AodBurnInViewModel,
75 ) {
76     @Composable
SmallClocknull77     fun ContentScope.SmallClock(
78         burnInParams: BurnInParameters,
79         onTopChanged: (top: Float?) -> Unit,
80         modifier: Modifier = Modifier,
81     ) {
82         val currentClock by viewModel.currentClock.collectAsStateWithLifecycle()
83         val smallTopMargin by
84             viewModel.smallClockTopMargin.collectAsStateWithLifecycle(
85                 viewModel.getSmallClockTopMargin()
86             )
87         if (currentClock?.smallClock?.view == null) {
88             return
89         }
90 
91         ClockView(
92             checkNotNull(currentClock).smallClock.view,
93             modifier =
94                 modifier
95                     .height(dimensionResource(R.dimen.small_clock_height))
96                     .padding(horizontal = dimensionResource(R.dimen.clock_padding_start))
97                     .padding(top = { smallTopMargin })
98                     .onTopPlacementChanged(onTopChanged)
99                     .burnInAware(viewModel = aodBurnInViewModel, params = burnInParams)
100                     .element(smallClockElementKey),
101         )
102     }
103 
104     @Composable
ContentScopenull105     fun ContentScope.LargeClock(burnInParams: BurnInParameters, modifier: Modifier = Modifier) {
106         val currentClock by viewModel.currentClock.collectAsStateWithLifecycle()
107         if (currentClock?.largeClock?.view == null) {
108             return
109         }
110 
111         // Centering animation for clocks that have custom position animations.
112         LaunchedEffect(layoutState.currentTransition?.progress) {
113             val transition = layoutState.currentTransition ?: return@LaunchedEffect
114             if (currentClock?.largeClock?.config?.hasCustomPositionUpdatedAnimation != true) {
115                 return@LaunchedEffect
116             }
117 
118             // If we are not doing the centering animation, do not animate.
119             val progress =
120                 if (transition.isTransitioningBetween(largeClockScene, splitShadeLargeClockScene)) {
121                     transition.progress
122                 } else {
123                     1f
124                 }
125 
126             val dir = if (transition.toContent == splitShadeLargeClockScene) -1f else 1f
127             val distance = dir * getClockCenteringDistance()
128             val largeClock = checkNotNull(currentClock).largeClock
129             largeClock.animations.onPositionUpdated(distance = distance, fraction = progress)
130         }
131 
132         Element(key = largeClockElementKey, modifier = modifier) {
133             ClockView(
134                 checkNotNull(currentClock).largeClock.view,
135                 modifier =
136                     Modifier.fillMaxSize()
137                         .burnInAware(
138                             viewModel = aodBurnInViewModel,
139                             params = burnInParams,
140                             isClock = true,
141                         ),
142             )
143         }
144     }
145 
getClockCenteringDistancenull146     fun getClockCenteringDistance(): Float {
147         return Resources.getSystem().displayMetrics.widthPixels / 4f
148     }
149 }
150