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