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

Verified Commit 012e2fb5 authored by Saalim Quadri's avatar Saalim Quadri
Browse files

feat: Add animator in buttonState changes

parent c1200c1d
Loading
Loading
Loading
Loading
Loading
+94 −43
Original line number Diff line number Diff line
@@ -17,6 +17,12 @@

package foundation.e.apps.ui.compose.components

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
@@ -32,6 +38,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Star
@@ -265,63 +272,107 @@ private fun PrivacyBadge(
            modifier = Modifier.clickable(onClick = onShowMoreClick),
        )
        return
    } else {
        // render the primary action button
    }

    val accentColor = MaterialTheme.colorScheme.tertiary
    val animationDuration = 250

    val targetContainerColor = when {
        uiState.isFilledStyle && uiState.enabled -> accentColor
        uiState.isFilledStyle -> accentColor.copy(alpha = 0.12f)
        else -> Color.Transparent
    }

    val targetBorderColor = when {
        uiState.isFilledStyle -> Color.Transparent
        uiState.enabled -> accentColor
        else -> accentColor.copy(alpha = 0.38f)
    }

    val labelTextColor = when {
    val targetTextColor = when {
        uiState.isFilledStyle -> MaterialTheme.colorScheme.onPrimary
        else -> accentColor
    }

    val buttonContent: @Composable () -> Unit = {
        val showSpinner = uiState.isInProgress && uiState.label.isBlank()
        if (showSpinner) {
            CircularProgressIndicator(
                modifier = Modifier.size(16.dp),
                strokeWidth = 2.dp,
                color = labelTextColor,
    val animatedContainerColor = animateColorAsState(
        targetValue = targetContainerColor,
        animationSpec = tween(durationMillis = animationDuration),
        label = "containerColor"
    )
        } else {
            Text(
                text = uiState.label,
                maxLines = 1,
                overflow = TextOverflow.Clip,
                color = labelTextColor,

    val animatedBorderColor = animateColorAsState(
        targetValue = targetBorderColor,
        animationSpec = tween(durationMillis = animationDuration),
        label = "borderColor"
    )

    val animatedTextColor = animateColorAsState(
        targetValue = targetTextColor,
        animationSpec = tween(durationMillis = animationDuration),
        label = "textColor"
    )
        }
    }

    Column(horizontalAlignment = Alignment.End) {
        val borderColor = when {
            uiState.isFilledStyle -> Color.Transparent
            uiState.enabled -> accentColor
            else -> accentColor.copy(alpha = 0.38f)
        }
        Button(
            onClick = onPrimaryClick,
            enabled = uiState.enabled,
            modifier = Modifier
                .widthIn(min = 88.dp)
                .height(40.dp),
            shape = RoundedCornerShape(4.dp),
            colors = ButtonDefaults.buttonColors(
                containerColor = when {
                    uiState.isFilledStyle -> accentColor
                    else -> Color.Transparent
                },
                contentColor = labelTextColor,
                disabledContainerColor = when {
                    uiState.isFilledStyle -> accentColor.copy(alpha = 0.12f)
                    else -> Color.Transparent
                },
                disabledContentColor = labelTextColor.copy(alpha = 0.38f),
                containerColor = animatedContainerColor.value,
                contentColor = animatedTextColor.value,
                disabledContainerColor = animatedContainerColor.value,
                disabledContentColor = animatedTextColor.value.copy(alpha = 0.38f),
            ),
            border = BorderStroke(1.dp, borderColor),
            border = BorderStroke(1.dp, animatedBorderColor.value),
            contentPadding = ButtonDefaults.ContentPadding,
        ) {
            buttonContent()
            val showSpinner = uiState.isInProgress && uiState.label.isBlank()
            val isProgressLabel = uiState.label.endsWith("%")
            // Stable keys here
            val animationKey = when {
                showSpinner -> "spinner"
                isProgressLabel -> "progress"
                else -> uiState.label
            }
            AnimatedContent(
                targetState = animationKey,
                transitionSpec = {
                    fadeIn(animationSpec = tween(animationDuration)) togetherWith
                            fadeOut(animationSpec = tween(animationDuration))
                },
                label = "buttonContent"
            ) { targetKey ->
                when (targetKey) {
                    "spinner" -> {
                        CircularProgressIndicator(
                            modifier = Modifier.size(16.dp),
                            strokeWidth = 2.dp,
                            color = animatedTextColor.value,
                        )
                    }

                    "progress" -> {
                        Text(
                            text = uiState.label,
                            maxLines = 1,
                            overflow = TextOverflow.Clip,
                            color = animatedTextColor.value,
                        )
                    }

                    else -> {
                        Text(
                            text = targetKey,
                            maxLines = 1,
                            overflow = TextOverflow.Clip,
                            color = animatedTextColor.value,
                        )
                    }
                }
            }
        }

        if (showPrivacyScore) {
+11 −1
Original line number Diff line number Diff line
@@ -211,7 +211,17 @@ fun mapAppToInstallState(
            }
        }

        Status.QUEUED, Status.AWAITING, Status.DOWNLOADING, Status.DOWNLOADED -> InstallButtonState(
        Status.QUEUED, Status.AWAITING -> InstallButtonState(
            label = ButtonLabel(resId = R.string.cancel),
            progressPercentText = null,
            enabled = true,
            style = styleFor(status, enabled = true),
            actionIntent = InstallButtonAction.CancelDownload,
            statusTag = StatusTag.Downloading,
            rawStatus = status,
        )

        Status.DOWNLOADING, Status.DOWNLOADED -> InstallButtonState(
            label = ButtonLabel(resId = if (percentLabel == null) R.string.cancel else null, text = percentLabel),
            progressPercentText = percentLabel,
            enabled = true,