Loading packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt 0 → 100644 +81 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.keyguard.ui.composable.blueprint import androidx.compose.ui.layout.HorizontalAlignmentLine import androidx.compose.ui.layout.VerticalAlignmentLine import kotlin.math.max import kotlin.math.min /** * Encapsulates all blueprint alignment lines. * * These can be used to communicate alignment lines emitted by elements that the blueprint should * consume and use to know how to constrain and/or place other elements in that blueprint. * * For more information, please see * [the official documentation](https://developer.android.com/jetpack/compose/layouts/alignment-lines). */ object BlueprintAlignmentLines { /** * Encapsulates alignment lines produced by the lock icon element. * * Because the lock icon is also the same element as the under-display fingerprint sensor * (UDFPS), blueprints should use its alignment lines to make sure that other elements on screen * do not overlap with the lock icon. */ object LockIcon { /** The left edge of the lock icon. */ val Left = VerticalAlignmentLine( merger = { old, new -> // When two left alignment line values are provided, choose the leftmost one: min(old, new) }, ) /** The top edge of the lock icon. */ val Top = HorizontalAlignmentLine( merger = { old, new -> // When two top alignment line values are provided, choose the topmost one: min(old, new) }, ) /** The right edge of the lock icon. */ val Right = VerticalAlignmentLine( merger = { old, new -> // When two right alignment line values are provided, choose the rightmost one: max(old, new) }, ) /** The bottom edge of the lock icon. */ val Bottom = HorizontalAlignmentLine( merger = { old, new -> // When two bottom alignment line values are provided, choose the bottommost // one: max(old, new) }, ) } } packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt +91 −39 Original line number Diff line number Diff line Loading @@ -16,18 +16,13 @@ package com.android.systemui.keyguard.ui.composable.blueprint import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.offset import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.height import com.android.compose.modifiers.width import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection import com.android.systemui.keyguard.ui.composable.section.ClockSection Loading Loading @@ -62,15 +57,13 @@ constructor( @Composable override fun SceneScope.Content(modifier: Modifier) { val context = LocalContext.current val lockIconBounds = lockSection.lockIconBounds(context) val isUdfpsVisible = viewModel.isUdfpsVisible Box( modifier = modifier, ) { Layout( content = { // Constrained to above the lock icon. Column( modifier = Modifier.fillMaxWidth().height { lockIconBounds.top }, modifier = Modifier.fillMaxWidth(), ) { with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) } with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) } Loading @@ -86,23 +79,82 @@ constructor( } } with(lockSection) { LockIcon( modifier = Modifier.width { lockIconBounds.width() } .height { lockIconBounds.height() } .offset { IntOffset(lockIconBounds.left, lockIconBounds.top) } ) } with(lockSection) { LockIcon() } Column(modifier = Modifier.fillMaxWidth().align(Alignment.BottomCenter)) { // Aligned to bottom and constrained to below the lock icon. Column(modifier = Modifier.fillMaxWidth()) { if (isUdfpsVisible) { with(ambientIndicationSection) { AmbientIndication(modifier = Modifier.fillMaxWidth()) } } with(bottomAreaSection) { BottomArea(modifier = Modifier.fillMaxWidth()) } with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) } } // Aligned to bottom and NOT constrained by the lock icon. with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = true) Shortcut(isStart = false, applyPadding = true) } }, modifier = modifier, ) { measurables, constraints -> check(measurables.size == 5) val ( aboveLockIconMeasurable, lockIconMeasurable, belowLockIconMeasurable, startShortcutMeasurable, endShortcutMeasurable, ) = measurables val noMinConstraints = constraints.copy( minWidth = 0, minHeight = 0, ) val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints) val lockIconBounds = IntRect( left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left], top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top], right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right], bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom], ) val aboveLockIconPlaceable = aboveLockIconMeasurable.measure( noMinConstraints.copy(maxHeight = lockIconBounds.top) ) val belowLockIconPlaceable = belowLockIconMeasurable.measure( noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom) ) val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints) val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints) layout(constraints.maxWidth, constraints.maxHeight) { aboveLockIconPlaceable.place( x = 0, y = 0, ) lockIconPlaceable.place( x = lockIconBounds.left, y = lockIconBounds.top, ) belowLockIconPlaceable.place( x = 0, y = constraints.maxHeight - belowLockIconPlaceable.height, ) startShortcutPleaceable.place( x = 0, y = constraints.maxHeight - startShortcutPleaceable.height, ) endShortcutPleaceable.place( x = constraints.maxWidth - endShortcutPleaceable.width, y = constraints.maxHeight - endShortcutPleaceable.height, ) } } } Loading packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt +92 −94 Original line number Diff line number Diff line Loading @@ -16,24 +16,13 @@ package com.android.systemui.keyguard.ui.composable.blueprint import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.height import com.android.compose.modifiers.width import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection import com.android.systemui.keyguard.ui.composable.section.ClockSection Loading @@ -42,12 +31,10 @@ import com.android.systemui.keyguard.ui.composable.section.NotificationSection import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection import com.android.systemui.keyguard.ui.composable.section.StatusBarSection import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel import com.android.systemui.res.R import dagger.Binds import dagger.Module import dagger.multibindings.IntoSet import javax.inject.Inject import kotlin.math.roundToInt /** * Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form Loading @@ -70,15 +57,13 @@ constructor( @Composable override fun SceneScope.Content(modifier: Modifier) { val context = LocalContext.current val lockIconBounds = lockSection.lockIconBounds(context) val isUdfpsVisible = viewModel.isUdfpsVisible Box( modifier = modifier, ) { Layout( content = { // Constrained to above the lock icon. Column( modifier = Modifier.fillMaxWidth().height { lockIconBounds.top }, modifier = Modifier.fillMaxWidth(), ) { with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) } with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) } Loading @@ -94,72 +79,85 @@ constructor( } } val shortcutSizePx = with(LocalDensity.current) { bottomAreaSection.shortcutSizeDp().toSize() } Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().offset { val rowTop = if (shortcutSizePx.height > lockIconBounds.height()) { (lockIconBounds.top - (shortcutSizePx.height + lockIconBounds.height()) / 2) .roundToInt() } else { lockIconBounds.top } IntOffset(0, rowTop) }, ) { Spacer(Modifier.weight(1f)) with(bottomAreaSection) { Shortcut(isStart = true) } Spacer(Modifier.weight(1f)) with(lockSection) { LockIcon( modifier = Modifier.width { lockIconBounds.width() } .height { lockIconBounds.height() } ) } Spacer(Modifier.weight(1f)) // Constrained to the left of the lock icon (in left-to-right layouts). with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) } with(bottomAreaSection) { Shortcut(isStart = false) } with(lockSection) { LockIcon() } Spacer(Modifier.weight(1f)) } // Constrained to the right of the lock icon (in left-to-right layouts). with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) } Column(modifier = Modifier.fillMaxWidth().align(Alignment.BottomCenter)) { // Aligned to bottom and constrained to below the lock icon. Column(modifier = Modifier.fillMaxWidth()) { if (isUdfpsVisible) { with(ambientIndicationSection) { AmbientIndication(modifier = Modifier.fillMaxWidth()) } } with(bottomAreaSection) { IndicationArea( modifier = Modifier.fillMaxWidth() .padding( horizontal = dimensionResource( R.dimen.keyguard_affordance_horizontal_offset with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) } } }, modifier = modifier, ) { measurables, constraints -> check(measurables.size == 5) val ( aboveLockIconMeasurable, startSideShortcutMeasurable, lockIconMeasurable, endSideShortcutMeasurable, belowLockIconMeasurable, ) = measurables val noMinConstraints = constraints.copy( minWidth = 0, minHeight = 0, ) val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints) val lockIconBounds = IntRect( left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left], top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top], right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right], bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom], ) val aboveLockIconPlaceable = aboveLockIconMeasurable.measure( noMinConstraints.copy(maxHeight = lockIconBounds.top) ) .padding( bottom = dimensionResource( R.dimen.keyguard_affordance_vertical_offset val startSideShortcutPlaceable = startSideShortcutMeasurable.measure(noMinConstraints) val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints) val belowLockIconPlaceable = belowLockIconMeasurable.measure( noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom) ) layout(constraints.maxWidth, constraints.maxHeight) { aboveLockIconPlaceable.place( x = 0, y = 0, ) .heightIn(min = shortcutSizeDp().height), startSideShortcutPlaceable.placeRelative( x = lockIconBounds.left / 2 - startSideShortcutPlaceable.width / 2, y = lockIconBounds.center.y - startSideShortcutPlaceable.height / 2, ) lockIconPlaceable.place( x = lockIconBounds.left, y = lockIconBounds.top, ) endSideShortcutPlaceable.placeRelative( x = lockIconBounds.right + (constraints.maxWidth - lockIconBounds.right) / 2 - endSideShortcutPlaceable.width / 2, y = lockIconBounds.center.y - endSideShortcutPlaceable.height / 2, ) belowLockIconPlaceable.place( x = 0, y = constraints.maxHeight - belowLockIconPlaceable.height, ) } } } } Loading packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt +23 −31 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.composable.section import android.view.View import android.widget.ImageView import androidx.annotation.IdRes import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size Loading Loading @@ -58,38 +57,17 @@ constructor( private val indicationAreaViewModel: KeyguardIndicationAreaViewModel, private val keyguardRootViewModel: KeyguardRootViewModel, ) { @Composable fun SceneScope.BottomArea( modifier: Modifier = Modifier, ) { Row( modifier = modifier .padding( horizontal = dimensionResource(R.dimen.keyguard_affordance_horizontal_offset) ) .padding( bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset) ), ) { Shortcut( isStart = true, ) IndicationArea( modifier = Modifier.weight(1f), ) Shortcut( isStart = false, ) } } /** * Renders a single lockscreen shortcut. * * @param isStart Whether the shortcut goes on the left (in left-to-right locales). * @param applyPadding Whether to apply padding around the shortcut, this is needed if the * shortcut is placed along the edges of the display. */ @Composable fun SceneScope.Shortcut( isStart: Boolean, applyPadding: Boolean, modifier: Modifier = Modifier, ) { MovableElement( Loading @@ -103,6 +81,12 @@ constructor( falsingManager = falsingManager, vibratorHelper = vibratorHelper, indicationController = indicationController, modifier = if (applyPadding) { Modifier.shortcutPadding() } else { Modifier } ) } } Loading @@ -113,7 +97,7 @@ constructor( ) { MovableElement( key = IndicationAreaElementKey, modifier = modifier, modifier = modifier.shortcutPadding(), ) { IndicationArea( indicationAreaViewModel = indicationAreaViewModel, Loading Loading @@ -218,6 +202,14 @@ constructor( modifier = modifier.fillMaxWidth(), ) } @Composable private fun Modifier.shortcutPadding(): Modifier { return this.padding( horizontal = dimensionResource(R.dimen.keyguard_affordance_horizontal_offset) ) .padding(bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset)) } } private val StartButtonElementKey = ElementKey("StartButton") Loading packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt +59 −33 Original line number Diff line number Diff line Loading @@ -17,8 +17,6 @@ package com.android.systemui.keyguard.ui.composable.section import android.content.Context import android.graphics.Point import android.graphics.Rect import android.util.DisplayMetrics import android.view.WindowManager import androidx.compose.foundation.background Loading @@ -28,11 +26,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.layout import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.SceneScope import com.android.systemui.biometrics.AuthController import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines import com.android.systemui.res.R import javax.inject.Inject Loading @@ -49,8 +53,33 @@ constructor( key = LockIconElementKey, modifier = modifier, ) { val context = LocalContext.current Box( modifier = Modifier.background(Color.Red), modifier = Modifier.background(Color.Red).layout { measurable, _ -> val lockIconBounds = lockIconBounds(context) val placeable = measurable.measure( Constraints.fixed( width = lockIconBounds.width, height = lockIconBounds.height, ) ) layout( width = placeable.width, height = placeable.height, alignmentLines = mapOf( BlueprintAlignmentLines.LockIcon.Left to lockIconBounds.left, BlueprintAlignmentLines.LockIcon.Top to lockIconBounds.top, BlueprintAlignmentLines.LockIcon.Right to lockIconBounds.right, BlueprintAlignmentLines.LockIcon.Bottom to lockIconBounds.bottom, ), ) { placeable.place(0, 0) } }, ) { Text( text = "TODO(b/316211368): Lock", Loading @@ -67,9 +96,9 @@ constructor( * On devices that support UDFPS (under-display fingerprint sensor), the bounds of the icon are * the same as the bounds of the sensor. */ fun lockIconBounds( private fun lockIconBounds( context: Context, ): Rect { ): IntRect { val windowViewBounds = windowManager.currentWindowMetrics.bounds var widthPx = windowViewBounds.right.toFloat() if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) { Loading @@ -85,36 +114,33 @@ constructor( val lockIconRadiusPx = (defaultDensity * 36).toInt() val udfpsLocation = authController.udfpsLocation return if (authController.isUdfpsSupported && udfpsLocation != null) { centerLockIcon(udfpsLocation, authController.udfpsRadius) val (center, radius) = if (authController.isUdfpsSupported && udfpsLocation != null) { Pair( IntOffset( x = udfpsLocation.x, y = udfpsLocation.y, ), authController.udfpsRadius.toInt(), ) } else { val scaleFactor = authController.scaleFactor val bottomPaddingPx = context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom) val heightPx = windowViewBounds.bottom.toFloat() centerLockIcon( Point( (widthPx / 2).toInt(), (heightPx - ((bottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt() Pair( IntOffset( x = (widthPx / 2).toInt(), y = (heightPx - ((bottomPaddingPx + lockIconRadiusPx) * scaleFactor)) .toInt(), ), lockIconRadiusPx * scaleFactor (lockIconRadiusPx * scaleFactor).toInt(), ) } } private fun centerLockIcon( center: Point, radius: Float, ): Rect { return Rect().apply { set( center.x - radius.toInt(), center.y - radius.toInt(), center.x + radius.toInt(), center.y + radius.toInt(), ) } return IntRect(center, radius) } } Loading Loading
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt 0 → 100644 +81 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.keyguard.ui.composable.blueprint import androidx.compose.ui.layout.HorizontalAlignmentLine import androidx.compose.ui.layout.VerticalAlignmentLine import kotlin.math.max import kotlin.math.min /** * Encapsulates all blueprint alignment lines. * * These can be used to communicate alignment lines emitted by elements that the blueprint should * consume and use to know how to constrain and/or place other elements in that blueprint. * * For more information, please see * [the official documentation](https://developer.android.com/jetpack/compose/layouts/alignment-lines). */ object BlueprintAlignmentLines { /** * Encapsulates alignment lines produced by the lock icon element. * * Because the lock icon is also the same element as the under-display fingerprint sensor * (UDFPS), blueprints should use its alignment lines to make sure that other elements on screen * do not overlap with the lock icon. */ object LockIcon { /** The left edge of the lock icon. */ val Left = VerticalAlignmentLine( merger = { old, new -> // When two left alignment line values are provided, choose the leftmost one: min(old, new) }, ) /** The top edge of the lock icon. */ val Top = HorizontalAlignmentLine( merger = { old, new -> // When two top alignment line values are provided, choose the topmost one: min(old, new) }, ) /** The right edge of the lock icon. */ val Right = VerticalAlignmentLine( merger = { old, new -> // When two right alignment line values are provided, choose the rightmost one: max(old, new) }, ) /** The bottom edge of the lock icon. */ val Bottom = HorizontalAlignmentLine( merger = { old, new -> // When two bottom alignment line values are provided, choose the bottommost // one: max(old, new) }, ) } }
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt +91 −39 Original line number Diff line number Diff line Loading @@ -16,18 +16,13 @@ package com.android.systemui.keyguard.ui.composable.blueprint import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.offset import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.height import com.android.compose.modifiers.width import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection import com.android.systemui.keyguard.ui.composable.section.ClockSection Loading Loading @@ -62,15 +57,13 @@ constructor( @Composable override fun SceneScope.Content(modifier: Modifier) { val context = LocalContext.current val lockIconBounds = lockSection.lockIconBounds(context) val isUdfpsVisible = viewModel.isUdfpsVisible Box( modifier = modifier, ) { Layout( content = { // Constrained to above the lock icon. Column( modifier = Modifier.fillMaxWidth().height { lockIconBounds.top }, modifier = Modifier.fillMaxWidth(), ) { with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) } with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) } Loading @@ -86,23 +79,82 @@ constructor( } } with(lockSection) { LockIcon( modifier = Modifier.width { lockIconBounds.width() } .height { lockIconBounds.height() } .offset { IntOffset(lockIconBounds.left, lockIconBounds.top) } ) } with(lockSection) { LockIcon() } Column(modifier = Modifier.fillMaxWidth().align(Alignment.BottomCenter)) { // Aligned to bottom and constrained to below the lock icon. Column(modifier = Modifier.fillMaxWidth()) { if (isUdfpsVisible) { with(ambientIndicationSection) { AmbientIndication(modifier = Modifier.fillMaxWidth()) } } with(bottomAreaSection) { BottomArea(modifier = Modifier.fillMaxWidth()) } with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) } } // Aligned to bottom and NOT constrained by the lock icon. with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = true) Shortcut(isStart = false, applyPadding = true) } }, modifier = modifier, ) { measurables, constraints -> check(measurables.size == 5) val ( aboveLockIconMeasurable, lockIconMeasurable, belowLockIconMeasurable, startShortcutMeasurable, endShortcutMeasurable, ) = measurables val noMinConstraints = constraints.copy( minWidth = 0, minHeight = 0, ) val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints) val lockIconBounds = IntRect( left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left], top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top], right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right], bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom], ) val aboveLockIconPlaceable = aboveLockIconMeasurable.measure( noMinConstraints.copy(maxHeight = lockIconBounds.top) ) val belowLockIconPlaceable = belowLockIconMeasurable.measure( noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom) ) val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints) val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints) layout(constraints.maxWidth, constraints.maxHeight) { aboveLockIconPlaceable.place( x = 0, y = 0, ) lockIconPlaceable.place( x = lockIconBounds.left, y = lockIconBounds.top, ) belowLockIconPlaceable.place( x = 0, y = constraints.maxHeight - belowLockIconPlaceable.height, ) startShortcutPleaceable.place( x = 0, y = constraints.maxHeight - startShortcutPleaceable.height, ) endShortcutPleaceable.place( x = constraints.maxWidth - endShortcutPleaceable.width, y = constraints.maxHeight - endShortcutPleaceable.height, ) } } } Loading
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt +92 −94 Original line number Diff line number Diff line Loading @@ -16,24 +16,13 @@ package com.android.systemui.keyguard.ui.composable.blueprint import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.height import com.android.compose.modifiers.width import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection import com.android.systemui.keyguard.ui.composable.section.ClockSection Loading @@ -42,12 +31,10 @@ import com.android.systemui.keyguard.ui.composable.section.NotificationSection import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection import com.android.systemui.keyguard.ui.composable.section.StatusBarSection import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel import com.android.systemui.res.R import dagger.Binds import dagger.Module import dagger.multibindings.IntoSet import javax.inject.Inject import kotlin.math.roundToInt /** * Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form Loading @@ -70,15 +57,13 @@ constructor( @Composable override fun SceneScope.Content(modifier: Modifier) { val context = LocalContext.current val lockIconBounds = lockSection.lockIconBounds(context) val isUdfpsVisible = viewModel.isUdfpsVisible Box( modifier = modifier, ) { Layout( content = { // Constrained to above the lock icon. Column( modifier = Modifier.fillMaxWidth().height { lockIconBounds.top }, modifier = Modifier.fillMaxWidth(), ) { with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) } with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) } Loading @@ -94,72 +79,85 @@ constructor( } } val shortcutSizePx = with(LocalDensity.current) { bottomAreaSection.shortcutSizeDp().toSize() } Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().offset { val rowTop = if (shortcutSizePx.height > lockIconBounds.height()) { (lockIconBounds.top - (shortcutSizePx.height + lockIconBounds.height()) / 2) .roundToInt() } else { lockIconBounds.top } IntOffset(0, rowTop) }, ) { Spacer(Modifier.weight(1f)) with(bottomAreaSection) { Shortcut(isStart = true) } Spacer(Modifier.weight(1f)) with(lockSection) { LockIcon( modifier = Modifier.width { lockIconBounds.width() } .height { lockIconBounds.height() } ) } Spacer(Modifier.weight(1f)) // Constrained to the left of the lock icon (in left-to-right layouts). with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) } with(bottomAreaSection) { Shortcut(isStart = false) } with(lockSection) { LockIcon() } Spacer(Modifier.weight(1f)) } // Constrained to the right of the lock icon (in left-to-right layouts). with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) } Column(modifier = Modifier.fillMaxWidth().align(Alignment.BottomCenter)) { // Aligned to bottom and constrained to below the lock icon. Column(modifier = Modifier.fillMaxWidth()) { if (isUdfpsVisible) { with(ambientIndicationSection) { AmbientIndication(modifier = Modifier.fillMaxWidth()) } } with(bottomAreaSection) { IndicationArea( modifier = Modifier.fillMaxWidth() .padding( horizontal = dimensionResource( R.dimen.keyguard_affordance_horizontal_offset with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) } } }, modifier = modifier, ) { measurables, constraints -> check(measurables.size == 5) val ( aboveLockIconMeasurable, startSideShortcutMeasurable, lockIconMeasurable, endSideShortcutMeasurable, belowLockIconMeasurable, ) = measurables val noMinConstraints = constraints.copy( minWidth = 0, minHeight = 0, ) val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints) val lockIconBounds = IntRect( left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left], top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top], right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right], bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom], ) val aboveLockIconPlaceable = aboveLockIconMeasurable.measure( noMinConstraints.copy(maxHeight = lockIconBounds.top) ) .padding( bottom = dimensionResource( R.dimen.keyguard_affordance_vertical_offset val startSideShortcutPlaceable = startSideShortcutMeasurable.measure(noMinConstraints) val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints) val belowLockIconPlaceable = belowLockIconMeasurable.measure( noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom) ) layout(constraints.maxWidth, constraints.maxHeight) { aboveLockIconPlaceable.place( x = 0, y = 0, ) .heightIn(min = shortcutSizeDp().height), startSideShortcutPlaceable.placeRelative( x = lockIconBounds.left / 2 - startSideShortcutPlaceable.width / 2, y = lockIconBounds.center.y - startSideShortcutPlaceable.height / 2, ) lockIconPlaceable.place( x = lockIconBounds.left, y = lockIconBounds.top, ) endSideShortcutPlaceable.placeRelative( x = lockIconBounds.right + (constraints.maxWidth - lockIconBounds.right) / 2 - endSideShortcutPlaceable.width / 2, y = lockIconBounds.center.y - endSideShortcutPlaceable.height / 2, ) belowLockIconPlaceable.place( x = 0, y = constraints.maxHeight - belowLockIconPlaceable.height, ) } } } } Loading
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt +23 −31 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.composable.section import android.view.View import android.widget.ImageView import androidx.annotation.IdRes import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size Loading Loading @@ -58,38 +57,17 @@ constructor( private val indicationAreaViewModel: KeyguardIndicationAreaViewModel, private val keyguardRootViewModel: KeyguardRootViewModel, ) { @Composable fun SceneScope.BottomArea( modifier: Modifier = Modifier, ) { Row( modifier = modifier .padding( horizontal = dimensionResource(R.dimen.keyguard_affordance_horizontal_offset) ) .padding( bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset) ), ) { Shortcut( isStart = true, ) IndicationArea( modifier = Modifier.weight(1f), ) Shortcut( isStart = false, ) } } /** * Renders a single lockscreen shortcut. * * @param isStart Whether the shortcut goes on the left (in left-to-right locales). * @param applyPadding Whether to apply padding around the shortcut, this is needed if the * shortcut is placed along the edges of the display. */ @Composable fun SceneScope.Shortcut( isStart: Boolean, applyPadding: Boolean, modifier: Modifier = Modifier, ) { MovableElement( Loading @@ -103,6 +81,12 @@ constructor( falsingManager = falsingManager, vibratorHelper = vibratorHelper, indicationController = indicationController, modifier = if (applyPadding) { Modifier.shortcutPadding() } else { Modifier } ) } } Loading @@ -113,7 +97,7 @@ constructor( ) { MovableElement( key = IndicationAreaElementKey, modifier = modifier, modifier = modifier.shortcutPadding(), ) { IndicationArea( indicationAreaViewModel = indicationAreaViewModel, Loading Loading @@ -218,6 +202,14 @@ constructor( modifier = modifier.fillMaxWidth(), ) } @Composable private fun Modifier.shortcutPadding(): Modifier { return this.padding( horizontal = dimensionResource(R.dimen.keyguard_affordance_horizontal_offset) ) .padding(bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset)) } } private val StartButtonElementKey = ElementKey("StartButton") Loading
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt +59 −33 Original line number Diff line number Diff line Loading @@ -17,8 +17,6 @@ package com.android.systemui.keyguard.ui.composable.section import android.content.Context import android.graphics.Point import android.graphics.Rect import android.util.DisplayMetrics import android.view.WindowManager import androidx.compose.foundation.background Loading @@ -28,11 +26,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.layout import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.SceneScope import com.android.systemui.biometrics.AuthController import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines import com.android.systemui.res.R import javax.inject.Inject Loading @@ -49,8 +53,33 @@ constructor( key = LockIconElementKey, modifier = modifier, ) { val context = LocalContext.current Box( modifier = Modifier.background(Color.Red), modifier = Modifier.background(Color.Red).layout { measurable, _ -> val lockIconBounds = lockIconBounds(context) val placeable = measurable.measure( Constraints.fixed( width = lockIconBounds.width, height = lockIconBounds.height, ) ) layout( width = placeable.width, height = placeable.height, alignmentLines = mapOf( BlueprintAlignmentLines.LockIcon.Left to lockIconBounds.left, BlueprintAlignmentLines.LockIcon.Top to lockIconBounds.top, BlueprintAlignmentLines.LockIcon.Right to lockIconBounds.right, BlueprintAlignmentLines.LockIcon.Bottom to lockIconBounds.bottom, ), ) { placeable.place(0, 0) } }, ) { Text( text = "TODO(b/316211368): Lock", Loading @@ -67,9 +96,9 @@ constructor( * On devices that support UDFPS (under-display fingerprint sensor), the bounds of the icon are * the same as the bounds of the sensor. */ fun lockIconBounds( private fun lockIconBounds( context: Context, ): Rect { ): IntRect { val windowViewBounds = windowManager.currentWindowMetrics.bounds var widthPx = windowViewBounds.right.toFloat() if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) { Loading @@ -85,36 +114,33 @@ constructor( val lockIconRadiusPx = (defaultDensity * 36).toInt() val udfpsLocation = authController.udfpsLocation return if (authController.isUdfpsSupported && udfpsLocation != null) { centerLockIcon(udfpsLocation, authController.udfpsRadius) val (center, radius) = if (authController.isUdfpsSupported && udfpsLocation != null) { Pair( IntOffset( x = udfpsLocation.x, y = udfpsLocation.y, ), authController.udfpsRadius.toInt(), ) } else { val scaleFactor = authController.scaleFactor val bottomPaddingPx = context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom) val heightPx = windowViewBounds.bottom.toFloat() centerLockIcon( Point( (widthPx / 2).toInt(), (heightPx - ((bottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt() Pair( IntOffset( x = (widthPx / 2).toInt(), y = (heightPx - ((bottomPaddingPx + lockIconRadiusPx) * scaleFactor)) .toInt(), ), lockIconRadiusPx * scaleFactor (lockIconRadiusPx * scaleFactor).toInt(), ) } } private fun centerLockIcon( center: Point, radius: Float, ): Rect { return Rect().apply { set( center.x - radius.toInt(), center.y - radius.toInt(), center.x + radius.toInt(), center.y + radius.toInt(), ) } return IntRect(center, radius) } } Loading