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.view.View
20 import android.widget.ImageView
21 import androidx.annotation.IdRes
22 import androidx.compose.foundation.layout.fillMaxWidth
23 import androidx.compose.foundation.layout.padding
24 import androidx.compose.foundation.layout.size
25 import androidx.compose.runtime.Composable
26 import androidx.compose.runtime.mutableStateOf
27 import androidx.compose.ui.Modifier
28 import androidx.compose.ui.res.dimensionResource
29 import androidx.compose.ui.unit.DpSize
30 import androidx.compose.ui.viewinterop.AndroidView
31 import androidx.core.content.res.ResourcesCompat
32 import com.android.compose.animation.scene.ElementKey
33 import com.android.compose.animation.scene.SceneScope
34 import com.android.systemui.animation.view.LaunchableImageView
35 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
36 import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
37 import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
38 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
39 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
40 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
41 import com.android.systemui.plugins.FalsingManager
42 import com.android.systemui.res.R
43 import com.android.systemui.statusbar.KeyguardIndicationController
44 import com.android.systemui.statusbar.VibratorHelper
45 import javax.inject.Inject
46 import kotlinx.coroutines.DisposableHandle
47 import kotlinx.coroutines.flow.Flow
48 
49 class BottomAreaSection
50 @Inject
51 constructor(
52     private val viewModel: KeyguardQuickAffordancesCombinedViewModel,
53     private val falsingManager: FalsingManager,
54     private val vibratorHelper: VibratorHelper,
55     private val indicationController: KeyguardIndicationController,
56     private val indicationAreaViewModel: KeyguardIndicationAreaViewModel,
57 ) {
58     /**
59      * Renders a single lockscreen shortcut.
60      *
61      * @param isStart Whether the shortcut goes on the left (in left-to-right locales).
62      * @param applyPadding Whether to apply padding around the shortcut, this is needed if the
63      *   shortcut is placed along the edges of the display.
64      */
65     @Composable
66     fun SceneScope.Shortcut(
67         isStart: Boolean,
68         applyPadding: Boolean,
69         modifier: Modifier = Modifier,
70     ) {
71         MovableElement(
72             key = if (isStart) StartButtonElementKey else EndButtonElementKey,
73             modifier = modifier,
74         ) {
75             content {
76                 Shortcut(
77                     viewId = if (isStart) R.id.start_button else R.id.end_button,
78                     viewModel = if (isStart) viewModel.startButton else viewModel.endButton,
79                     transitionAlpha = viewModel.transitionAlpha,
80                     falsingManager = falsingManager,
81                     vibratorHelper = vibratorHelper,
82                     indicationController = indicationController,
83                     modifier =
84                         if (applyPadding) {
85                             Modifier.shortcutPadding()
86                         } else {
87                             Modifier
88                         }
89                 )
90             }
91         }
92     }
93 
94     @Composable
95     fun SceneScope.IndicationArea(
96         modifier: Modifier = Modifier,
97     ) {
98         MovableElement(
99             key = IndicationAreaElementKey,
100             modifier = modifier.shortcutPadding(),
101         ) {
102             content {
103                 IndicationArea(
104                     indicationAreaViewModel = indicationAreaViewModel,
105                     indicationController = indicationController,
106                 )
107             }
108         }
109     }
110 
111     @Composable
112     fun shortcutSizeDp(): DpSize {
113         return DpSize(
114             width = dimensionResource(R.dimen.keyguard_affordance_fixed_width),
115             height = dimensionResource(R.dimen.keyguard_affordance_fixed_height),
116         )
117     }
118 
119     @Composable
120     private fun Shortcut(
121         @IdRes viewId: Int,
122         viewModel: Flow<KeyguardQuickAffordanceViewModel>,
123         transitionAlpha: Flow<Float>,
124         falsingManager: FalsingManager,
125         vibratorHelper: VibratorHelper,
126         indicationController: KeyguardIndicationController,
127         modifier: Modifier = Modifier,
128     ) {
129         val (binding, setBinding) = mutableStateOf<KeyguardQuickAffordanceViewBinder.Binding?>(null)
130 
131         AndroidView(
132             factory = { context ->
133                 val padding =
134                     context.resources.getDimensionPixelSize(
135                         R.dimen.keyguard_affordance_fixed_padding
136                     )
137                 val view =
138                     LaunchableImageView(context, null).apply {
139                         id = viewId
140                         scaleType = ImageView.ScaleType.FIT_CENTER
141                         background =
142                             ResourcesCompat.getDrawable(
143                                 context.resources,
144                                 R.drawable.keyguard_bottom_affordance_bg,
145                                 context.theme
146                             )
147                         foreground =
148                             ResourcesCompat.getDrawable(
149                                 context.resources,
150                                 R.drawable.keyguard_bottom_affordance_selected_border,
151                                 context.theme
152                             )
153                         visibility = View.INVISIBLE
154                         setPadding(padding, padding, padding, padding)
155                     }
156 
157                 setBinding(
158                     KeyguardQuickAffordanceViewBinder.bind(
159                         view,
160                         viewModel,
161                         transitionAlpha,
162                         falsingManager,
163                         vibratorHelper,
164                     ) {
165                         indicationController.showTransientIndication(it)
166                     }
167                 )
168 
169                 view
170             },
171             onRelease = { binding?.destroy() },
172             modifier =
173                 modifier.size(
174                     width = shortcutSizeDp().width,
175                     height = shortcutSizeDp().height,
176                 )
177         )
178     }
179 
180     @Composable
181     private fun IndicationArea(
182         indicationAreaViewModel: KeyguardIndicationAreaViewModel,
183         indicationController: KeyguardIndicationController,
184         modifier: Modifier = Modifier,
185     ) {
186         val (disposable, setDisposable) = mutableStateOf<DisposableHandle?>(null)
187 
188         AndroidView(
189             factory = { context ->
190                 val view = KeyguardIndicationArea(context, null)
191                 setDisposable(
192                     KeyguardIndicationAreaBinder.bind(
193                         view = view,
194                         viewModel = indicationAreaViewModel,
195                         indicationController = indicationController,
196                     )
197                 )
198                 view
199             },
200             onRelease = { disposable?.dispose() },
201             modifier = modifier.fillMaxWidth(),
202         )
203     }
204 
205     @Composable
206     private fun Modifier.shortcutPadding(): Modifier {
207         return this.padding(
208                 horizontal = dimensionResource(R.dimen.keyguard_affordance_horizontal_offset)
209             )
210             .padding(bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset))
211     }
212 }
213 
214 private val StartButtonElementKey = ElementKey("StartButton")
215 private val EndButtonElementKey = ElementKey("EndButton")
216 private val IndicationAreaElementKey = ElementKey("IndicationArea")
217