mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-01-17 16:23:57 +00:00
feat: Make patcher screen design more consistent with inspiration (#2805)
This commit is contained in:
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user