[flexiglass] Introduces use of alignment lines.
Alignment lines are used to communicate the position of the lock icon
and for new custom layout logic in both the default and shortcuts-beside-UDFPS
blueprints to avoid placing other elements on top of the lock icon.
This acts as a proof of concept, proving that we could also use
alignment lines from the clock section to communicate the same such that
blueprints can place elements above, below, or around various clock
elements (some clocks, like the weather clock, use multiple clock
elements and need other lockscreen elements to not overlap them).
Bug: 316211368
Test: manually verified on both blueprints
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT
Change-Id: Ic014e30e8b354b1952992f67f091d4da20c61923
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt
new file mode 100644
index 0000000..efa8cc7
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt
@@ -0,0 +1,81 @@
+/*
+ * 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)
+ },
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index fc1df84..7314453 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -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
@@ -62,47 +57,104 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- val context = LocalContext.current
- val lockIconBounds = lockSection.lockIconBounds(context)
val isUdfpsVisible = viewModel.isUdfpsVisible
- Box(
+ Layout(
+ content = {
+ // Constrained to above the lock icon.
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
+ with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ with(notificationSection) {
+ Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+ }
+ if (!isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
+
+ with(lockSection) { LockIcon() }
+
+ // 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()) }
+ }
+
+ // Aligned to bottom and NOT constrained by the lock icon.
+ with(bottomAreaSection) {
+ Shortcut(isStart = true, applyPadding = true)
+ Shortcut(isStart = false, applyPadding = true)
+ }
+ },
modifier = modifier,
- ) {
- Column(
- modifier = Modifier.fillMaxWidth().height { lockIconBounds.top },
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
- with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
- with(notificationSection) {
- Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
- }
- if (!isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
+ ) { measurables, constraints ->
+ check(measurables.size == 5)
+ val (
+ aboveLockIconMeasurable,
+ lockIconMeasurable,
+ belowLockIconMeasurable,
+ startShortcutMeasurable,
+ endShortcutMeasurable,
+ ) = measurables
- with(lockSection) {
- LockIcon(
- modifier =
- Modifier.width { lockIconBounds.width() }
- .height { lockIconBounds.height() }
- .offset { IntOffset(lockIconBounds.left, lockIconBounds.top) }
+ 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],
+ )
- Column(modifier = Modifier.fillMaxWidth().align(Alignment.BottomCenter)) {
- if (isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
+ 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)
- with(bottomAreaSection) { BottomArea(modifier = Modifier.fillMaxWidth()) }
+ 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,
+ )
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index fa913f1..4c119c7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -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
@@ -42,12 +31,10 @@
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
@@ -70,96 +57,107 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- val context = LocalContext.current
- val lockIconBounds = lockSection.lockIconBounds(context)
val isUdfpsVisible = viewModel.isUdfpsVisible
- Box(
+ Layout(
+ content = {
+ // Constrained to above the lock icon.
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
+ with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ with(notificationSection) {
+ Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+ }
+ if (!isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
+
+ // Constrained to the left of the lock icon (in left-to-right layouts).
+ with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) }
+
+ with(lockSection) { LockIcon() }
+
+ // Constrained to the right of the lock icon (in left-to-right layouts).
+ with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) }
+
+ // 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()) }
+ }
+ },
modifier = modifier,
- ) {
- Column(
- modifier = Modifier.fillMaxWidth().height { lockIconBounds.top },
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
- with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
- with(notificationSection) {
- Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
- }
- if (!isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
+ ) { measurables, constraints ->
+ check(measurables.size == 5)
+ val (
+ aboveLockIconMeasurable,
+ startSideShortcutMeasurable,
+ lockIconMeasurable,
+ endSideShortcutMeasurable,
+ belowLockIconMeasurable,
+ ) = measurables
- val shortcutSizePx =
- with(LocalDensity.current) { bottomAreaSection.shortcutSizeDp().toSize() }
+ val noMinConstraints =
+ constraints.copy(
+ minWidth = 0,
+ minHeight = 0,
+ )
- 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
- }
+ 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],
+ )
- IntOffset(0, rowTop)
- },
- ) {
- Spacer(Modifier.weight(1f))
+ val aboveLockIconPlaceable =
+ aboveLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ )
+ val startSideShortcutPlaceable = startSideShortcutMeasurable.measure(noMinConstraints)
+ val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints)
+ val belowLockIconPlaceable =
+ belowLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom)
+ )
- with(bottomAreaSection) { Shortcut(isStart = true) }
-
- Spacer(Modifier.weight(1f))
-
- with(lockSection) {
- LockIcon(
- modifier =
- Modifier.width { lockIconBounds.width() }
- .height { lockIconBounds.height() }
- )
- }
-
- Spacer(Modifier.weight(1f))
-
- with(bottomAreaSection) { Shortcut(isStart = false) }
-
- Spacer(Modifier.weight(1f))
- }
-
- Column(modifier = Modifier.fillMaxWidth().align(Alignment.BottomCenter)) {
- if (isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
-
- with(bottomAreaSection) {
- IndicationArea(
- modifier =
- Modifier.fillMaxWidth()
- .padding(
- horizontal =
- dimensionResource(
- R.dimen.keyguard_affordance_horizontal_offset
- )
- )
- .padding(
- bottom =
- dimensionResource(
- R.dimen.keyguard_affordance_vertical_offset
- )
- )
- .heightIn(min = shortcutSizeDp().height),
- )
- }
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ aboveLockIconPlaceable.place(
+ x = 0,
+ y = 0,
+ )
+ 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,
+ )
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 53e4be3..db20f65 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -19,7 +19,6 @@
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
@@ -58,38 +57,17 @@
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(
@@ -103,6 +81,12 @@
falsingManager = falsingManager,
vibratorHelper = vibratorHelper,
indicationController = indicationController,
+ modifier =
+ if (applyPadding) {
+ Modifier.shortcutPadding()
+ } else {
+ Modifier
+ }
)
}
}
@@ -113,7 +97,7 @@
) {
MovableElement(
key = IndicationAreaElementKey,
- modifier = modifier,
+ modifier = modifier.shortcutPadding(),
) {
IndicationArea(
indicationAreaViewModel = indicationAreaViewModel,
@@ -218,6 +202,14 @@
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")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index 8bbe424b..d93863d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -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
@@ -28,11 +26,17 @@
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
@@ -49,8 +53,33 @@
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",
@@ -67,9 +96,9 @@
* 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)) {
@@ -85,36 +114,33 @@
val lockIconRadiusPx = (defaultDensity * 36).toInt()
val udfpsLocation = authController.udfpsLocation
- return if (authController.isUdfpsSupported && udfpsLocation != null) {
- centerLockIcon(udfpsLocation, authController.udfpsRadius)
- } else {
- val scaleFactor = authController.scaleFactor
- val bottomPaddingPx =
- context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
- val heightPx = windowViewBounds.bottom.toFloat()
+ 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()
- ),
- lockIconRadiusPx * scaleFactor
- )
- }
- }
+ Pair(
+ IntOffset(
+ x = (widthPx / 2).toInt(),
+ y =
+ (heightPx - ((bottomPaddingPx + lockIconRadiusPx) * scaleFactor))
+ .toInt(),
+ ),
+ (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)
}
}