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

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

Change-Id: Ic203df62eee550e4415e8290bad4d335a1d1f69b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Mike Schneider
2023-07-05 13:05:56 +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",
"androidx.compose.runtime_runtime",
"androidx.compose.animation_animation-graphics",
"androidx.compose.material3_material3",
"androidx.activity_activity-compose",
],

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
@file:OptIn(ExperimentalAnimationApi::class)
@file:OptIn(ExperimentalAnimationApi::class, ExperimentalAnimationGraphicsApi::class)
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.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.keyframes
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
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.layout.Box
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.geometry.CornerRadius
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.Constraints
@@ -70,6 +75,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings
import com.android.compose.grid.VerticalGrid
import com.android.internal.R.id.image
import com.android.systemui.R
import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance
import com.android.systemui.bouncer.ui.viewmodel.EnteredKey
@@ -139,7 +145,8 @@ private fun PinInputDisplay(viewModel: PinBouncerViewModel) {
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) {
// Remove entry from visiblePinEntries once the hide transition completed.
@@ -171,15 +178,11 @@ private sealed class EntryVisibility {
}
@Composable
private fun ObscuredInputEntry(transition: Transition<EntryVisibility>) {
private fun PinInputEntry(shapeResourceId: Int, transition: Transition<EntryVisibility>) {
// spec: http://shortn/_DEhE3Xl2bi
val shapePadding = 6.dp
val shapeOvershootSize = 22.dp
val dismissStaggerDelayMs = 33
val dismissDurationMs = 450
val expansionDurationMs = 250
val shapeExpandDurationMs = 83
val shapeRetractDurationMs = 167
val shapeCollapseDurationMs = 200
val animatedEntryWidth by
@@ -194,19 +197,17 @@ private fun ObscuredInputEntry(transition: Transition<EntryVisibility>) {
},
label = "entry space"
) { state ->
if (state == EntryVisibility.Shown) entryShapeSize + (shapePadding * 2) else 0.dp
if (state == EntryVisibility.Shown) entryShapeSize else 0.dp
}
val animatedShapeSize by
transition.animateDp(
transitionSpec = {
when {
EntryVisibility.Hidden isTransitioningTo EntryVisibility.Shown ->
keyframes {
durationMillis = shapeExpandDurationMs + shapeRetractDurationMs
0.dp at 0 with Easings.Linear
shapeOvershootSize at shapeExpandDurationMs with Easings.Legacy
}
EntryVisibility.Hidden isTransitioningTo EntryVisibility.Shown -> {
// The AVD contains the entry transition.
snap()
}
targetState is EntryVisibility.BulkHidden -> {
val target = targetState as EntryVisibility.BulkHidden
tween(
@@ -220,17 +221,21 @@ private fun ObscuredInputEntry(transition: Transition<EntryVisibility>) {
},
label = "shape size"
) { state ->
when (state) {
EntryVisibility.Shown -> entryShapeSize
else -> 0.dp
}
if (state == EntryVisibility.Shown) entryShapeSize else 0.dp
}
val dotColor = MaterialTheme.colorScheme.onSurfaceVariant
Layout(
content = {
// TODO(b/282730134): add support for dot shapes.
Canvas(Modifier) { drawCircle(dotColor) }
val image = AnimatedImageVector.animatedVectorResource(shapeResourceId)
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, _ ->
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 pinButtonErrorShrinkFactor = 67.dp / pinButtonSize

View File

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

View File

@@ -16,6 +16,8 @@
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.bouncer.domain.interactor.BouncerInteractor
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. */
class PinBouncerViewModel(
applicationContext: Context,
private val applicationScope: CoroutineScope,
private val interactor: BouncerInteractor,
isInputEnabled: StateFlow<Boolean>,
@@ -37,6 +40,8 @@ class PinBouncerViewModel(
isInputEnabled = isInputEnabled,
) {
val pinShapes = PinShapeAdapter(applicationContext)
private val mutablePinEntries = MutableStateFlow<List<EnteredKey>>(emptyList())
val pinEntries: StateFlow<List<EnteredKey>> = mutablePinEntries

View File

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

View File

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