feat: Make patcher screen design more consistent with inspiration (#2805)

This commit is contained in:
Ushie
2025-12-17 22:58:02 +03:00
committed by GitHub
parent e0d529c2df
commit dbb6c01e89
2 changed files with 102 additions and 88 deletions

View File

@@ -1,7 +1,7 @@
package app.revanced.manager.ui.component.patcher package app.revanced.manager.ui.component.patcher
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateColorAsState import androidx.compose.animation.Crossfade
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Cancel import androidx.compose.material.icons.filled.Cancel
import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.CheckCircle
@@ -21,6 +20,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -53,20 +53,11 @@ fun Steps(
category: StepCategory, category: StepCategory,
steps: List<Step>, steps: List<Step>,
stepCount: Pair<Int, Int>? = null, stepCount: Pair<Int, Int>? = null,
stepProgressProvider: StepProgressProvider stepProgressProvider: StepProgressProvider,
isExpanded: Boolean = false,
onExpand: () -> Unit,
onClick: () -> Unit
) { ) {
var expanded by rememberSaveable { mutableStateOf(true) }
val categoryColor by animateColorAsState(
if (expanded) MaterialTheme.colorScheme.surfaceContainerHigh else Color.Transparent,
label = "category"
)
val cardColor by animateColorAsState(
if (expanded) MaterialTheme.colorScheme.surfaceContainer else Color.Transparent,
label = "card"
)
val state = remember(steps) { val state = remember(steps) {
when { when {
steps.all { it.state == State.COMPLETED } -> State.COMPLETED steps.all { it.state == State.COMPLETED } -> State.COMPLETED
@@ -76,48 +67,52 @@ fun Steps(
} }
} }
LaunchedEffect(state) {
if (state == State.RUNNING)
onExpand()
}
Column( Column(
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(16.dp)) .clip(MaterialTheme.shapes.large)
.fillMaxWidth() .fillMaxWidth()
.background(cardColor) .background(MaterialTheme.colorScheme.surfaceContainerLow)
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(14.dp),
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(16.dp)) .clickable(true, onClick = onClick)
.clickable { expanded = !expanded } .fillMaxWidth()
.background(categoryColor) .padding(20.dp)
) { ) {
Row( StepIcon(state = state, size = 24.dp)
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier.padding(16.dp)
) {
StepIcon(state = state, size = 24.dp)
Text(stringResource(category.displayName)) Text(stringResource(category.displayName))
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
val stepProgress = remember(stepCount, steps) { val stepProgress = remember(stepCount, steps) {
stepCount?.let { (current, total) -> "$current/$total" } stepCount?.let { (current, total) -> "$current/$total" }
?: "${steps.count { it.state == State.COMPLETED }}/${steps.size}" ?: "${steps.count { it.state == State.COMPLETED }}/${steps.size}"
}
Text(
text = stepProgress,
style = MaterialTheme.typography.labelSmall
)
ArrowButton(modifier = Modifier.size(24.dp), expanded = expanded, onClick = null)
} }
Text(
text = stepProgress,
style = MaterialTheme.typography.labelSmall
)
ArrowButton(modifier = Modifier.size(24.dp), expanded = isExpanded, onClick = null)
} }
AnimatedVisibility(visible = expanded) { AnimatedVisibility(visible = isExpanded) {
Column( Column(
modifier = Modifier.fillMaxWidth() modifier = Modifier
.background(MaterialTheme.colorScheme.background.copy(0.6f))
.fillMaxWidth()
.padding(top = 10.dp)
) { ) {
steps.forEach { step -> steps.forEachIndexed { index, step ->
val (progress, progressText) = when (step.progressKey) { val (progress, progressText) = when (step.progressKey) {
null -> null null -> null
ProgressKey.DOWNLOAD -> stepProgressProvider.downloadProgress?.let { (downloaded, total) -> ProgressKey.DOWNLOAD -> stepProgressProvider.downloadProgress?.let { (downloaded, total) ->
@@ -131,7 +126,9 @@ fun Steps(
state = step.state, state = step.state,
message = step.message, message = step.message,
progress = progress, progress = progress,
progressText = progressText progressText = progressText,
isFirst = index == 0,
isLast = index == steps.lastIndex,
) )
} }
} }
@@ -145,7 +142,9 @@ fun SubStep(
state: State, state: State,
message: String? = null, message: String? = null,
progress: Float? = null, progress: Float? = null,
progressText: String? = null progressText: String? = null,
isFirst: Boolean = false,
isLast: Boolean = false,
) { ) {
var messageExpanded by rememberSaveable { mutableStateOf(true) } var messageExpanded by rememberSaveable { mutableStateOf(true) }
@@ -156,22 +155,22 @@ fun SubStep(
clickable { messageExpanded = !messageExpanded } clickable { messageExpanded = !messageExpanded }
else this else this
} }
.padding(top = if (isFirst) 10.dp else 8.dp, bottom = if (isLast) 20.dp else 8.dp)
.padding(horizontal = 20.dp)
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
) { ) {
Box( StepIcon(
modifier = Modifier.size(24.dp), size = 18.dp,
contentAlignment = Alignment.Center state = state,
) { progress = progress,
StepIcon(state, progress, size = 20.dp) )
}
Text( Text(
text = name, text = name,
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.labelLarge,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f, true), modifier = Modifier.weight(1f, true),
@@ -201,7 +200,7 @@ fun SubStep(
text = message.orEmpty(), text = message.orEmpty(),
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.secondary, color = MaterialTheme.colorScheme.secondary,
modifier = Modifier.padding(horizontal = 52.dp, vertical = 8.dp) modifier = Modifier.padding(horizontal = 36.dp, vertical = 8.dp)
) )
} }
} }
@@ -211,40 +210,44 @@ fun SubStep(
fun StepIcon(state: State, progress: Float? = null, size: Dp) { fun StepIcon(state: State, progress: Float? = null, size: Dp) {
val strokeWidth = Dp(floor(size.value / 10) + 1) val strokeWidth = Dp(floor(size.value / 10) + 1)
when (state) { Crossfade(targetState = state, label = "State CrossFade") { state ->
State.COMPLETED -> Icon( when (state) {
Icons.Filled.CheckCircle, State.COMPLETED -> Icon(
contentDescription = stringResource(R.string.step_completed), Icons.Filled.CheckCircle,
tint = MaterialTheme.colorScheme.surfaceTint, contentDescription = stringResource(R.string.step_completed),
modifier = Modifier.size(size) tint = Color(0xFF59B463),
) modifier = Modifier.size(size)
State.FAILED -> Icon(
Icons.Filled.Cancel,
contentDescription = stringResource(R.string.step_failed),
tint = MaterialTheme.colorScheme.error,
modifier = Modifier.size(size)
)
State.WAITING -> Icon(
Icons.Outlined.Circle,
contentDescription = stringResource(R.string.step_waiting),
tint = MaterialTheme.colorScheme.surfaceVariant,
modifier = Modifier.size(size)
)
State.RUNNING ->
LoadingIndicator(
modifier = stringResource(R.string.step_running).let { description ->
Modifier
.size(size)
.semantics {
contentDescription = description
}
},
progress = { progress },
strokeWidth = strokeWidth
) )
State.FAILED -> Icon(
Icons.Filled.Cancel,
contentDescription = stringResource(R.string.step_failed),
tint = MaterialTheme.colorScheme.error,
modifier = Modifier.size(size)
)
State.WAITING -> Icon(
Icons.Outlined.Circle,
contentDescription = stringResource(R.string.step_waiting),
tint = MaterialTheme.colorScheme.onSurface.copy(.2f),
modifier = Modifier.size(size)
)
State.RUNNING -> {
LoadingIndicator(
modifier = stringResource(R.string.step_running).let { description ->
Modifier
.size(size)
.semantics {
contentDescription = description
}
},
progress = { progress },
strokeWidth = strokeWidth
)
}
}
} }
} }

View File

@@ -213,6 +213,12 @@ fun PatcherScreen(
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize() .fillMaxSize()
) { ) {
var expandedCategory by rememberSaveable { mutableStateOf<StepCategory?>(null) }
val expandCategory: (StepCategory?) -> Unit = { category ->
expandedCategory = category
}
LinearProgressIndicator( LinearProgressIndicator(
progress = { viewModel.progress }, progress = { viewModel.progress },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
@@ -231,7 +237,12 @@ fun PatcherScreen(
category = category, category = category,
steps = steps, steps = steps,
stepCount = if (category == StepCategory.PATCHING) viewModel.patchesProgress else null, stepCount = if (category == StepCategory.PATCHING) viewModel.patchesProgress else null,
stepProgressProvider = viewModel stepProgressProvider = viewModel,
isExpanded = expandedCategory == category,
onExpand = { expandCategory(category) },
onClick = {
expandCategory(if (expandedCategory == category) null else category)
}
) )
} }
} }