Merge changes from topic "shapes" into udc-qpr-dev am: 1672c54654

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/23811467

Change-Id: I9d3f7bcdff55cf3467ea6fde6f9dad0d57933c3e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Mike Schneider
2023-07-05 12:45:46 +00:00
committed by Automerger Merge Worker
6 changed files with 37 additions and 23 deletions

View File

@@ -34,6 +34,7 @@ android_library {
"PlatformComposeCore", "PlatformComposeCore",
"androidx.compose.runtime_runtime", "androidx.compose.runtime_runtime",
"androidx.compose.animation_animation-graphics",
"androidx.compose.material3_material3", "androidx.compose.material3_material3",
"androidx.activity_activity-compose", "androidx.activity_activity-compose",
], ],

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
@file:OptIn(ExperimentalAnimationApi::class) @file:OptIn(ExperimentalAnimationApi::class, ExperimentalAnimationGraphicsApi::class)
package com.android.systemui.bouncer.ui.composable package com.android.systemui.bouncer.ui.composable
@@ -29,11 +29,14 @@ import androidx.compose.animation.core.Transition
import androidx.compose.animation.core.animateDp import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.keyframes
import androidx.compose.animation.core.snap import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.Canvas import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
import androidx.compose.animation.graphics.res.animatedVectorResource
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.foundation.Image
import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -61,8 +64,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Constraints
@@ -70,6 +75,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings import com.android.compose.animation.Easings
import com.android.compose.grid.VerticalGrid import com.android.compose.grid.VerticalGrid
import com.android.internal.R.id.image
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance
import com.android.systemui.bouncer.ui.viewmodel.EnteredKey import com.android.systemui.bouncer.ui.viewmodel.EnteredKey
@@ -139,7 +145,8 @@ private fun PinInputDisplay(viewModel: PinBouncerViewModel) {
else -> EntryVisibility.Hidden else -> EntryVisibility.Hidden
} }
ObscuredInputEntry(updateTransition(visibility, label = "Pin Entry $entry")) val shape = viewModel.pinShapes.getShape(entry.sequenceNumber)
PinInputEntry(shape, updateTransition(visibility, label = "Pin Entry $entry"))
LaunchedEffect(entry) { LaunchedEffect(entry) {
// Remove entry from visiblePinEntries once the hide transition completed. // Remove entry from visiblePinEntries once the hide transition completed.
@@ -171,15 +178,11 @@ private sealed class EntryVisibility {
} }
@Composable @Composable
private fun ObscuredInputEntry(transition: Transition<EntryVisibility>) { private fun PinInputEntry(shapeResourceId: Int, transition: Transition<EntryVisibility>) {
// spec: http://shortn/_DEhE3Xl2bi // spec: http://shortn/_DEhE3Xl2bi
val shapePadding = 6.dp
val shapeOvershootSize = 22.dp
val dismissStaggerDelayMs = 33 val dismissStaggerDelayMs = 33
val dismissDurationMs = 450 val dismissDurationMs = 450
val expansionDurationMs = 250 val expansionDurationMs = 250
val shapeExpandDurationMs = 83
val shapeRetractDurationMs = 167
val shapeCollapseDurationMs = 200 val shapeCollapseDurationMs = 200
val animatedEntryWidth by val animatedEntryWidth by
@@ -194,19 +197,17 @@ private fun ObscuredInputEntry(transition: Transition<EntryVisibility>) {
}, },
label = "entry space" label = "entry space"
) { state -> ) { state ->
if (state == EntryVisibility.Shown) entryShapeSize + (shapePadding * 2) else 0.dp if (state == EntryVisibility.Shown) entryShapeSize else 0.dp
} }
val animatedShapeSize by val animatedShapeSize by
transition.animateDp( transition.animateDp(
transitionSpec = { transitionSpec = {
when { when {
EntryVisibility.Hidden isTransitioningTo EntryVisibility.Shown -> EntryVisibility.Hidden isTransitioningTo EntryVisibility.Shown -> {
keyframes { // The AVD contains the entry transition.
durationMillis = shapeExpandDurationMs + shapeRetractDurationMs snap()
0.dp at 0 with Easings.Linear }
shapeOvershootSize at shapeExpandDurationMs with Easings.Legacy
}
targetState is EntryVisibility.BulkHidden -> { targetState is EntryVisibility.BulkHidden -> {
val target = targetState as EntryVisibility.BulkHidden val target = targetState as EntryVisibility.BulkHidden
tween( tween(
@@ -220,17 +221,21 @@ private fun ObscuredInputEntry(transition: Transition<EntryVisibility>) {
}, },
label = "shape size" label = "shape size"
) { state -> ) { state ->
when (state) { if (state == EntryVisibility.Shown) entryShapeSize else 0.dp
EntryVisibility.Shown -> entryShapeSize
else -> 0.dp
}
} }
val dotColor = MaterialTheme.colorScheme.onSurfaceVariant val dotColor = MaterialTheme.colorScheme.onSurfaceVariant
Layout( Layout(
content = { content = {
// TODO(b/282730134): add support for dot shapes. val image = AnimatedImageVector.animatedVectorResource(shapeResourceId)
Canvas(Modifier) { drawCircle(dotColor) } var atEnd by remember { mutableStateOf(false) }
Image(
painter = rememberAnimatedVectorPainter(image, atEnd),
contentDescription = null,
contentScale = ContentScale.Crop,
colorFilter = ColorFilter.tint(dotColor),
)
LaunchedEffect(Unit) { atEnd = true }
} }
) { measurables, _ -> ) { measurables, _ ->
val shapeSizePx = animatedShapeSize.roundToPx() val shapeSizePx = animatedShapeSize.roundToPx()
@@ -507,7 +512,7 @@ private suspend fun showFailureAnimation(
} }
} }
private val entryShapeSize = 16.dp private val entryShapeSize = 30.dp
private val pinButtonSize = 84.dp private val pinButtonSize = 84.dp
private val pinButtonErrorShrinkFactor = 67.dp / pinButtonSize private val pinButtonErrorShrinkFactor = 67.dp / pinButtonSize

View File

@@ -61,6 +61,7 @@ constructor(
private val pin: PinBouncerViewModel by lazy { private val pin: PinBouncerViewModel by lazy {
PinBouncerViewModel( PinBouncerViewModel(
applicationContext = applicationContext,
applicationScope = applicationScope, applicationScope = applicationScope,
interactor = interactor, interactor = interactor,
isInputEnabled = isInputEnabled, isInputEnabled = isInputEnabled,

View File

@@ -16,6 +16,8 @@
package com.android.systemui.bouncer.ui.viewmodel package com.android.systemui.bouncer.ui.viewmodel
import android.content.Context
import com.android.keyguard.PinShapeAdapter
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -29,6 +31,7 @@ import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the PIN code bouncer UI. */ /** Holds UI state and handles user input for the PIN code bouncer UI. */
class PinBouncerViewModel( class PinBouncerViewModel(
applicationContext: Context,
private val applicationScope: CoroutineScope, private val applicationScope: CoroutineScope,
private val interactor: BouncerInteractor, private val interactor: BouncerInteractor,
isInputEnabled: StateFlow<Boolean>, isInputEnabled: StateFlow<Boolean>,
@@ -37,6 +40,8 @@ class PinBouncerViewModel(
isInputEnabled = isInputEnabled, isInputEnabled = isInputEnabled,
) { ) {
val pinShapes = PinShapeAdapter(applicationContext)
private val mutablePinEntries = MutableStateFlow<List<EnteredKey>>(emptyList()) private val mutablePinEntries = MutableStateFlow<List<EnteredKey>>(emptyList())
val pinEntries: StateFlow<List<EnteredKey>> = mutablePinEntries val pinEntries: StateFlow<List<EnteredKey>> = mutablePinEntries

View File

@@ -42,6 +42,7 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() {
) )
private val underTest = private val underTest =
PinBouncerViewModel( PinBouncerViewModel(
applicationContext = context,
applicationScope = testScope.backgroundScope, applicationScope = testScope.backgroundScope,
interactor = interactor =
utils.bouncerInteractor( utils.bouncerInteractor(

View File

@@ -67,6 +67,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
) )
private val underTest = private val underTest =
PinBouncerViewModel( PinBouncerViewModel(
applicationContext = context,
applicationScope = testScope.backgroundScope, applicationScope = testScope.backgroundScope,
interactor = bouncerInteractor, interactor = bouncerInteractor,
isInputEnabled = MutableStateFlow(true).asStateFlow(), isInputEnabled = MutableStateFlow(true).asStateFlow(),