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.modifier
18 
19 import androidx.compose.runtime.Composable
20 import androidx.compose.runtime.LaunchedEffect
21 import androidx.compose.runtime.getValue
22 import androidx.compose.runtime.mutableFloatStateOf
23 import androidx.compose.runtime.remember
24 import androidx.compose.ui.Modifier
25 import androidx.compose.ui.graphics.graphicsLayer
26 import androidx.compose.ui.layout.boundsInWindow
27 import androidx.compose.ui.layout.onPlaced
28 import androidx.lifecycle.compose.collectAsStateWithLifecycle
29 import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
30 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
31 import com.android.systemui.keyguard.ui.viewmodel.BurnInScaleViewModel
32 import kotlinx.coroutines.flow.map
33 
34 /**
35  * Modifies the composable to account for anti-burn in translation, alpha, and scaling.
36  *
37  * Please override [isClock] as `true` if the composable is an element that's part of a clock.
38  */
39 @Composable
40 fun Modifier.burnInAware(
41     viewModel: AodBurnInViewModel,
42     params: BurnInParameters,
43     isClock: Boolean = false,
44 ): Modifier {
45     val cachedYTranslation = remember { mutableFloatStateOf(0f) }
46     LaunchedEffect(Unit) {
47         viewModel.updateBurnInParams(params.copy(translationY = { cachedYTranslation.floatValue }))
48     }
49 
50     val burnIn = viewModel.movement
51     val translationX by
52         burnIn.map { it.translationX.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
53     val translationY by
54         burnIn.map { it.translationY.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
55     cachedYTranslation.floatValue = translationY
56     val scaleViewModel by
57         burnIn
58             .map { BurnInScaleViewModel(scale = it.scale, scaleClockOnly = it.scaleClockOnly) }
59             .collectAsStateWithLifecycle(initialValue = BurnInScaleViewModel())
60 
61     return this.graphicsLayer {
62         this.translationX = if (isClock) 0F else translationX
63         this.translationY = translationY
64         this.alpha = alpha
65 
66         val scale = if (scaleViewModel.scaleClockOnly) scaleViewModel.scale else 1f
67         this.scaleX = scale
68         this.scaleY = scale
69     }
70 }
71 
72 /** Reports the "top" coordinate of the modified composable to the given [consumer]. */
73 @Composable
onTopPlacementChangednull74 fun Modifier.onTopPlacementChanged(consumer: (Float) -> Unit): Modifier {
75     return onPlaced { coordinates -> consumer(coordinates.boundsInWindow().top) }
76 }
77