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