Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 37bcd4aa authored by Mike Schneider's avatar Mike Schneider Committed by Automerger Merge Worker
Browse files

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

parents cd3a2f38 1672c546
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -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",
    ],
    ],
+28 −23
Original line number Original line Diff line number Diff line
@@ -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,18 +197,16 @@ 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
@@ -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
+1 −0
Original line number Original line Diff line number Diff line
@@ -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,
+5 −0
Original line number Original line Diff line number Diff line
@@ -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


+1 −0
Original line number Original line Diff line number Diff line
@@ -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(
Loading