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.blueprint 18 19 import androidx.compose.foundation.layout.Box 20 import androidx.compose.foundation.layout.Column 21 import androidx.compose.foundation.layout.fillMaxHeight 22 import androidx.compose.foundation.layout.fillMaxSize 23 import androidx.compose.foundation.layout.fillMaxWidth 24 import androidx.compose.runtime.Composable 25 import androidx.compose.runtime.getValue 26 import androidx.compose.ui.Alignment 27 import androidx.compose.ui.Modifier 28 import androidx.compose.ui.graphics.graphicsLayer 29 import androidx.compose.ui.layout.Layout 30 import androidx.compose.ui.unit.IntRect 31 import androidx.lifecycle.compose.collectAsStateWithLifecycle 32 import com.android.compose.animation.scene.SceneScope 33 import com.android.compose.modifiers.padding 34 import com.android.systemui.keyguard.ui.composable.LockscreenLongPress 35 import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection 36 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection 37 import com.android.systemui.keyguard.ui.composable.section.LockSection 38 import com.android.systemui.keyguard.ui.composable.section.NotificationSection 39 import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection 40 import com.android.systemui.keyguard.ui.composable.section.StatusBarSection 41 import com.android.systemui.keyguard.ui.composable.section.TopAreaSection 42 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel 43 import dagger.Binds 44 import dagger.Module 45 import dagger.multibindings.IntoSet 46 import java.util.Optional 47 import javax.inject.Inject 48 import kotlin.math.roundToInt 49 50 /** 51 * Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form 52 * factor). 53 */ 54 class ShortcutsBesideUdfpsBlueprint 55 @Inject 56 constructor( 57 private val viewModel: LockscreenContentViewModel, 58 private val statusBarSection: StatusBarSection, 59 private val lockSection: LockSection, 60 private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>, 61 private val bottomAreaSection: BottomAreaSection, 62 private val settingsMenuSection: SettingsMenuSection, 63 private val topAreaSection: TopAreaSection, 64 private val notificationSection: NotificationSection, 65 ) : ComposableLockscreenSceneBlueprint { 66 67 override val id: String = "shortcuts-besides-udfps" 68 69 @Composable 70 override fun SceneScope.Content(modifier: Modifier) { 71 val isUdfpsVisible = viewModel.isUdfpsVisible 72 val shouldUseSplitNotificationShade by 73 viewModel.shouldUseSplitNotificationShade.collectAsStateWithLifecycle() 74 val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle() 75 76 LockscreenLongPress( 77 viewModel = viewModel.longPress, 78 modifier = modifier, 79 ) { onSettingsMenuPlaced -> 80 Layout( 81 content = { 82 // Constrained to above the lock icon. 83 Column( 84 modifier = Modifier.fillMaxSize(), 85 ) { 86 with(statusBarSection) { 87 StatusBar( 88 modifier = 89 Modifier.fillMaxWidth() 90 .padding( 91 horizontal = { unfoldTranslations.start.roundToInt() }, 92 ) 93 ) 94 } 95 96 Box { 97 with(topAreaSection) { 98 DefaultClockLayout( 99 modifier = 100 Modifier.graphicsLayer { 101 translationX = unfoldTranslations.start 102 }, 103 ) 104 } 105 if (shouldUseSplitNotificationShade) { 106 with(notificationSection) { 107 Notifications( 108 burnInParams = null, 109 modifier = 110 Modifier.fillMaxWidth(0.5f) 111 .fillMaxHeight() 112 .align(alignment = Alignment.TopEnd) 113 ) 114 } 115 } 116 } 117 if (!shouldUseSplitNotificationShade) { 118 with(notificationSection) { 119 Notifications( 120 burnInParams = null, 121 modifier = Modifier.weight(weight = 1f) 122 ) 123 } 124 } 125 if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) { 126 with(ambientIndicationSectionOptional.get()) { 127 AmbientIndication(modifier = Modifier.fillMaxWidth()) 128 } 129 } 130 } 131 132 // Constrained to the left of the lock icon (in left-to-right layouts). 133 with(bottomAreaSection) { 134 Shortcut( 135 isStart = true, 136 applyPadding = false, 137 modifier = 138 Modifier.graphicsLayer { translationX = unfoldTranslations.start }, 139 ) 140 } 141 142 with(lockSection) { LockIcon() } 143 144 // Constrained to the right of the lock icon (in left-to-right layouts). 145 with(bottomAreaSection) { 146 Shortcut( 147 isStart = false, 148 applyPadding = false, 149 modifier = 150 Modifier.graphicsLayer { translationX = unfoldTranslations.end }, 151 ) 152 } 153 154 // Aligned to bottom and constrained to below the lock icon. 155 Column(modifier = Modifier.fillMaxWidth()) { 156 if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) { 157 with(ambientIndicationSectionOptional.get()) { 158 AmbientIndication(modifier = Modifier.fillMaxWidth()) 159 } 160 } 161 162 with(bottomAreaSection) { 163 IndicationArea(modifier = Modifier.fillMaxWidth()) 164 } 165 } 166 167 // Aligned to bottom and NOT constrained by the lock icon. 168 with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) } 169 }, 170 modifier = Modifier.fillMaxSize(), 171 ) { measurables, constraints -> 172 check(measurables.size == 6) 173 val aboveLockIconMeasurable = measurables[0] 174 val startSideShortcutMeasurable = measurables[1] 175 val lockIconMeasurable = measurables[2] 176 val endSideShortcutMeasurable = measurables[3] 177 val belowLockIconMeasurable = measurables[4] 178 val settingsMenuMeasurable = measurables[5] 179 180 val noMinConstraints = 181 constraints.copy( 182 minWidth = 0, 183 minHeight = 0, 184 ) 185 186 val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints) 187 val lockIconBounds = 188 IntRect( 189 left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left], 190 top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top], 191 right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right], 192 bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom], 193 ) 194 195 val aboveLockIconPlaceable = 196 aboveLockIconMeasurable.measure( 197 noMinConstraints.copy(maxHeight = lockIconBounds.top) 198 ) 199 val startSideShortcutPlaceable = 200 startSideShortcutMeasurable.measure(noMinConstraints) 201 val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints) 202 val belowLockIconPlaceable = 203 belowLockIconMeasurable.measure( 204 noMinConstraints.copy( 205 maxHeight = constraints.maxHeight - lockIconBounds.bottom 206 ) 207 ) 208 val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints) 209 210 layout(constraints.maxWidth, constraints.maxHeight) { 211 aboveLockIconPlaceable.place( 212 x = 0, 213 y = 0, 214 ) 215 startSideShortcutPlaceable.placeRelative( 216 x = lockIconBounds.left / 2 - startSideShortcutPlaceable.width / 2, 217 y = lockIconBounds.center.y - startSideShortcutPlaceable.height / 2, 218 ) 219 lockIconPlaceable.place( 220 x = lockIconBounds.left, 221 y = lockIconBounds.top, 222 ) 223 endSideShortcutPlaceable.placeRelative( 224 x = 225 lockIconBounds.right + 226 (constraints.maxWidth - lockIconBounds.right) / 2 - 227 endSideShortcutPlaceable.width / 2, 228 y = lockIconBounds.center.y - endSideShortcutPlaceable.height / 2, 229 ) 230 belowLockIconPlaceable.place( 231 x = 0, 232 y = constraints.maxHeight - belowLockIconPlaceable.height, 233 ) 234 settingsMenuPlaceable.place( 235 x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2, 236 y = constraints.maxHeight - settingsMenuPlaceable.height, 237 ) 238 } 239 } 240 } 241 } 242 } 243 244 @Module 245 interface ShortcutsBesideUdfpsBlueprintModule { 246 @Binds 247 @IntoSet blueprintnull248 fun blueprint(blueprint: ShortcutsBesideUdfpsBlueprint): ComposableLockscreenSceneBlueprint 249 } 250