mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-04-30 05:54:26 +02:00
feat: Improve update screen design (#2487)
This commit is contained in:
parent
d5c63ead26
commit
fc05f95837
@ -81,3 +81,41 @@ fun AppTopBar(
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AppTopBar(
|
||||
title: @Composable () -> Unit,
|
||||
onBackClick: (() -> Unit)? = null,
|
||||
backIcon: @Composable (() -> Unit) = @Composable {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(
|
||||
R.string.back
|
||||
)
|
||||
)
|
||||
},
|
||||
actions: @Composable (RowScope.() -> Unit) = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null,
|
||||
applyContainerColor: Boolean = false
|
||||
) {
|
||||
val containerColor = if (applyContainerColor) {
|
||||
MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp)
|
||||
} else {
|
||||
Color.Unspecified
|
||||
}
|
||||
|
||||
TopAppBar(
|
||||
title = title,
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = {
|
||||
if (onBackClick != null) {
|
||||
IconButton(onClick = onBackClick) {
|
||||
backIcon()
|
||||
}
|
||||
}
|
||||
},
|
||||
actions = actions,
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = containerColor
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -7,10 +7,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.CalendarToday
|
||||
import androidx.compose.material.icons.outlined.Campaign
|
||||
import androidx.compose.material.icons.outlined.FileDownload
|
||||
import androidx.compose.material.icons.outlined.Sell
|
||||
import androidx.compose.material.icons.outlined.NewReleases
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
@ -37,28 +34,18 @@ fun Changelog(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Campaign,
|
||||
imageVector = Icons.Outlined.NewReleases,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
)
|
||||
Text(
|
||||
version.removePrefix("v"),
|
||||
"${version.removePrefix("v")} ($publishDate)",
|
||||
style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight(800)),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Tag(
|
||||
Icons.Outlined.CalendarToday,
|
||||
publishDate
|
||||
)
|
||||
}
|
||||
}
|
||||
Markdown(
|
||||
markdown,
|
||||
|
@ -5,19 +5,17 @@ import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Cancel
|
||||
import androidx.compose.material.icons.outlined.InstallMobile
|
||||
import androidx.compose.material.icons.outlined.Update
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@ -28,16 +26,15 @@ import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.revanced.manager.BuildConfig
|
||||
import app.revanced.manager.R
|
||||
import app.revanced.manager.network.dto.ReVancedAsset
|
||||
import app.revanced.manager.ui.component.AppTopBar
|
||||
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
|
||||
import app.revanced.manager.ui.component.settings.Changelog
|
||||
import app.revanced.manager.ui.viewmodel.UpdateViewModel
|
||||
import app.revanced.manager.ui.viewmodel.UpdateViewModel.State
|
||||
@ -60,13 +57,64 @@ fun UpdateScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
AppTopBar(
|
||||
title = stringResource(R.string.update),
|
||||
title = {
|
||||
Column {
|
||||
Text(stringResource(vm.state.title))
|
||||
|
||||
if (vm.state == State.DOWNLOADING) {
|
||||
Text(
|
||||
text = "${vm.downloadedSize.div(1000000)} MB / ${
|
||||
vm.totalSize.div(1000000)
|
||||
} MB (${vm.downloadProgress.times(100).toInt()}%)",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.outline
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
onBackClick = onBackClick
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
val buttonConfig = when (vm.state) {
|
||||
State.CAN_DOWNLOAD -> Triple(
|
||||
{ vm.downloadUpdate() },
|
||||
R.string.download,
|
||||
Icons.Outlined.InstallMobile
|
||||
)
|
||||
|
||||
State.DOWNLOADING -> Triple(onBackClick, R.string.cancel, Icons.Outlined.Cancel)
|
||||
State.CAN_INSTALL -> Triple(
|
||||
{ vm.installUpdate() },
|
||||
R.string.install_app,
|
||||
Icons.Outlined.InstallMobile
|
||||
)
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
buttonConfig?.let { (onClick, textRes, icon) ->
|
||||
HapticExtendedFloatingActionButton(
|
||||
onClick = onClick::invoke,
|
||||
icon = { Icon(icon, null) },
|
||||
text = { Text(stringResource(textRes)) }
|
||||
)
|
||||
}
|
||||
|
||||
},
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues),
|
||||
) {
|
||||
if (vm.state == State.DOWNLOADING)
|
||||
LinearProgressIndicator(
|
||||
progress = { vm.downloadProgress },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
||||
AnimatedVisibility(visible = vm.showInternetCheckDialog) {
|
||||
MeteredDownloadConfirmationDialog(
|
||||
onDismiss = { vm.showInternetCheckDialog = false },
|
||||
@ -76,21 +124,14 @@ fun UpdateScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.padding(vertical = 16.dp, horizontal = 24.dp)
|
||||
.padding(16.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(32.dp)
|
||||
) {
|
||||
Header(
|
||||
vm.state,
|
||||
vm.releaseInfo,
|
||||
DownloadData(vm.downloadProgress, vm.downloadedSize, vm.totalSize)
|
||||
)
|
||||
vm.releaseInfo?.let { changelog ->
|
||||
HorizontalDivider()
|
||||
Changelog(changelog)
|
||||
} ?: Spacer(modifier = Modifier.weight(1f))
|
||||
Buttons(vm.state, vm::downloadUpdate, vm::installUpdate, onBackClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,58 +164,6 @@ private fun MeteredDownloadConfirmationDialog(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(state: State, releaseInfo: ReVancedAsset?, downloadData: DownloadData) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
Text(
|
||||
text = stringResource(state.title),
|
||||
style = MaterialTheme.typography.headlineMedium
|
||||
)
|
||||
if (state == State.CAN_DOWNLOAD) {
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.current_version,
|
||||
BuildConfig.VERSION_NAME
|
||||
),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
releaseInfo?.version?.let {
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.new_version,
|
||||
it.replace("v", "")
|
||||
),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (state == State.DOWNLOADING) {
|
||||
LinearProgressIndicator(
|
||||
progress = { downloadData.downloadProgress },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
Text(
|
||||
text =
|
||||
"${downloadData.downloadedSize.div(1000000)} MB / ${
|
||||
downloadData.totalSize.div(
|
||||
1000000
|
||||
)
|
||||
} MB (${
|
||||
downloadData.downloadProgress.times(
|
||||
100
|
||||
).toInt()
|
||||
}%)",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.outline,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.Changelog(releaseInfo: ReVancedAsset) {
|
||||
val scrollState = rememberScrollState()
|
||||
@ -206,39 +195,3 @@ private fun ColumnScope.Changelog(releaseInfo: ReVancedAsset) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Buttons(
|
||||
state: State,
|
||||
onDownloadClick: () -> Unit,
|
||||
onInstallClick: () -> Unit,
|
||||
onBackClick: () -> Unit
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
if (state.showCancel) {
|
||||
TextButton(
|
||||
onClick = onBackClick,
|
||||
) {
|
||||
Text(text = stringResource(R.string.cancel))
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
if (state == State.CAN_DOWNLOAD) {
|
||||
Button(onClick = onDownloadClick) {
|
||||
Text(text = stringResource(R.string.update))
|
||||
}
|
||||
} else if (state == State.CAN_INSTALL) {
|
||||
Button(
|
||||
onClick = onInstallClick
|
||||
) {
|
||||
Text(text = stringResource(R.string.install_app))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class DownloadData(
|
||||
val downloadProgress: Float,
|
||||
val downloadedSize: Long,
|
||||
val totalSize: Long
|
||||
)
|
@ -9,6 +9,7 @@ import android.content.pm.PackageInstaller
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableLongStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.core.content.ContextCompat
|
||||
@ -42,9 +43,9 @@ class UpdateViewModel(
|
||||
private val networkInfo: NetworkInfo by inject()
|
||||
private val fs: Filesystem by inject()
|
||||
|
||||
var downloadedSize by mutableStateOf(0L)
|
||||
var downloadedSize by mutableLongStateOf(0L)
|
||||
private set
|
||||
var totalSize by mutableStateOf(0L)
|
||||
var totalSize by mutableLongStateOf(0L)
|
||||
private set
|
||||
val downloadProgress by derivedStateOf {
|
||||
if (downloadedSize == 0L || totalSize == 0L) return@derivedStateOf 0f
|
||||
@ -89,7 +90,7 @@ class UpdateViewModel(
|
||||
totalSize = contentLength
|
||||
}
|
||||
}
|
||||
state = State.CAN_INSTALL
|
||||
installUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -140,10 +141,10 @@ class UpdateViewModel(
|
||||
location.delete()
|
||||
}
|
||||
|
||||
enum class State(@StringRes val title: Int, val showCancel: Boolean = false) {
|
||||
enum class State(@StringRes val title: Int) {
|
||||
CAN_DOWNLOAD(R.string.update_available),
|
||||
DOWNLOADING(R.string.downloading_manager_update, true),
|
||||
CAN_INSTALL(R.string.ready_to_install_update, true),
|
||||
DOWNLOADING(R.string.downloading_manager_update),
|
||||
CAN_INSTALL(R.string.ready_to_install_update),
|
||||
INSTALLING(R.string.installing_manager_update),
|
||||
FAILED(R.string.install_update_manager_failed),
|
||||
SUCCESS(R.string.update_completed)
|
||||
|
Loading…
x
Reference in New Issue
Block a user