CopyFeedbackOverlay.kt

3.2 KB · raw

1package com.peachpasswords.ui.components23import androidx.compose.animation.core.Animatable4import androidx.compose.animation.core.FastOutSlowInEasing5import androidx.compose.animation.core.tween6import androidx.compose.foundation.Canvas7import androidx.compose.foundation.background8import androidx.compose.foundation.layout.Box9import androidx.compose.foundation.layout.fillMaxSize10import androidx.compose.foundation.layout.padding11import androidx.compose.foundation.layout.size12import androidx.compose.foundation.layout.statusBarsPadding13import androidx.compose.foundation.shape.CircleShape14import androidx.compose.runtime.Composable15import androidx.compose.runtime.LaunchedEffect16import androidx.compose.runtime.collectAsState17import androidx.compose.runtime.getValue18import androidx.compose.runtime.remember19import androidx.compose.ui.Alignment20import androidx.compose.ui.Modifier21import androidx.compose.ui.draw.clip22import androidx.compose.ui.geometry.CornerRadius23import androidx.compose.ui.graphics.drawscope.Stroke24import androidx.compose.ui.graphics.graphicsLayer25import androidx.compose.ui.unit.dp26import com.peachpasswords.shared.CopyFeedbackBus27import com.peachpasswords.ui.theme.LocalPeachAccent28import com.peachpasswords.ui.theme.LocalReduceMotion29import kotlinx.coroutines.launch3031@Composable32fun CopyFeedbackOverlay(modifier: Modifier = Modifier) {33    val pulse by CopyFeedbackBus.pulses.collectAsState()34    val accent = LocalPeachAccent.current35    val reduceMotion = LocalReduceMotion.current36    val ringAlpha = remember { Animatable(0f) }37    val pipPulse = remember { Animatable(0f) }3839    LaunchedEffect(pulse, reduceMotion) {40        if (pulse == 0) return@LaunchedEffect41        if (reduceMotion) {42            ringAlpha.snapTo(0.42f)43            pipPulse.snapTo(1f)44            ringAlpha.animateTo(0f, tween(220))45            pipPulse.animateTo(0f, tween(220))46        } else {47            launch {48                ringAlpha.snapTo(0f)49                ringAlpha.animateTo(0.62f, tween(190, easing = FastOutSlowInEasing))50                ringAlpha.animateTo(0f, tween(530, easing = FastOutSlowInEasing))51            }52            launch {53                pipPulse.snapTo(0f)54                pipPulse.animateTo(1f, tween(160, easing = FastOutSlowInEasing))55                pipPulse.animateTo(0f, tween(240, easing = FastOutSlowInEasing))56            }57        }58    }5960    Box(modifier.fillMaxSize()) {61        Canvas(Modifier.fillMaxSize()) {62            val stroke = 1.5.dp.toPx()63            drawRoundRect(64                color = accent.copy(alpha = ringAlpha.value * 0.55f),65                style = Stroke(width = stroke),66                cornerRadius = CornerRadius(20.dp.toPx(), 20.dp.toPx()),67            )68        }69        Box(70            Modifier71                .align(Alignment.TopStart)72                .statusBarsPadding()73                .padding(start = 74.dp, top = 23.dp)74                .size(6.dp)75                .graphicsLayer {76                    val scale = 1f + (pipPulse.value * 1.2f)77                    scaleX = scale78                    scaleY = scale79                }80                .clip(CircleShape)81                .background(accent.copy(alpha = pipPulse.value)),82        )83    }84}