mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2024-11-10 01:01:56 +01:00
feat(Update Screen): changelogs & handle states (#1464)
Co-authored-by: Ax333l <main@axelen.xyz>
This commit is contained in:
parent
62a5fce66c
commit
bd9778a3d1
15 changed files with 545 additions and 273 deletions
|
@ -158,4 +158,7 @@ dependencies {
|
||||||
|
|
||||||
// Markdown
|
// Markdown
|
||||||
implementation(libs.markdown.renderer)
|
implementation(libs.markdown.renderer)
|
||||||
|
|
||||||
|
// Fading Edges
|
||||||
|
implementation(libs.fading.edges)
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ class MainActivity : ComponentActivity() {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
vm.dismissUpdateDialog()
|
vm.dismissUpdateDialog()
|
||||||
navController.navigate(Destination.Settings(SettingsDestination.UpdateProgress))
|
navController.navigate(Destination.Settings(SettingsDestination.Update(false)))
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.update))
|
Text(stringResource(R.string.update))
|
||||||
|
@ -85,8 +85,8 @@ class MainActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon = { Icon(Icons.Outlined.Update, null) },
|
icon = { Icon(Icons.Outlined.Update, null) },
|
||||||
title = { Text(stringResource(R.string.update_available)) },
|
title = { Text(stringResource(R.string.update_available_dialog_title)) },
|
||||||
text = { Text(stringResource(R.string.update_available_description, it)) }
|
text = { Text(stringResource(R.string.update_available_dialog_description, it)) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ val viewModelModule = module {
|
||||||
viewModelOf(::AppSelectorViewModel)
|
viewModelOf(::AppSelectorViewModel)
|
||||||
viewModelOf(::VersionSelectorViewModel)
|
viewModelOf(::VersionSelectorViewModel)
|
||||||
viewModelOf(::InstallerViewModel)
|
viewModelOf(::InstallerViewModel)
|
||||||
viewModelOf(::UpdateProgressViewModel)
|
viewModelOf(::UpdateViewModel)
|
||||||
viewModelOf(::ChangelogsViewModel)
|
viewModelOf(::ChangelogsViewModel)
|
||||||
viewModelOf(::ImportExportViewModel)
|
viewModelOf(::ImportExportViewModel)
|
||||||
viewModelOf(::ContributorViewModel)
|
viewModelOf(::ContributorViewModel)
|
||||||
|
|
|
@ -29,6 +29,7 @@ class InstallService : Service() {
|
||||||
else -> {
|
else -> {
|
||||||
sendBroadcast(Intent().apply {
|
sendBroadcast(Intent().apply {
|
||||||
action = APP_INSTALL_ACTION
|
action = APP_INSTALL_ACTION
|
||||||
|
`package` = packageName
|
||||||
putExtra(EXTRA_INSTALL_STATUS, extraStatus)
|
putExtra(EXTRA_INSTALL_STATUS, extraStatus)
|
||||||
putExtra(EXTRA_INSTALL_STATUS_MESSAGE, extraStatusMessage)
|
putExtra(EXTRA_INSTALL_STATUS_MESSAGE, extraStatusMessage)
|
||||||
putExtra(EXTRA_PACKAGE_NAME, extraPackageName)
|
putExtra(EXTRA_PACKAGE_NAME, extraPackageName)
|
||||||
|
|
|
@ -31,7 +31,7 @@ class UninstallService : Service() {
|
||||||
else -> {
|
else -> {
|
||||||
sendBroadcast(Intent().apply {
|
sendBroadcast(Intent().apply {
|
||||||
action = APP_UNINSTALL_ACTION
|
action = APP_UNINSTALL_ACTION
|
||||||
|
`package` = packageName
|
||||||
putExtra(EXTRA_UNINSTALL_STATUS, extraStatus)
|
putExtra(EXTRA_UNINSTALL_STATUS, extraStatus)
|
||||||
putExtra(EXTRA_UNINSTALL_STATUS_MESSAGE, extraStatusMessage)
|
putExtra(EXTRA_UNINSTALL_STATUS_MESSAGE, extraStatusMessage)
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
package app.revanced.manager.ui.component.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
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.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import app.revanced.manager.ui.component.Markdown
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Changelog(
|
||||||
|
markdown: String,
|
||||||
|
version: String,
|
||||||
|
downloadCount: String,
|
||||||
|
publishDate: String
|
||||||
|
) {
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 0.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Campaign,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(32.dp)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
version.removePrefix("v"),
|
||||||
|
style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight(800)),
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Tag(
|
||||||
|
Icons.Outlined.Sell,
|
||||||
|
version
|
||||||
|
)
|
||||||
|
Tag(
|
||||||
|
Icons.Outlined.FileDownload,
|
||||||
|
downloadCount
|
||||||
|
)
|
||||||
|
Tag(
|
||||||
|
Icons.Outlined.CalendarToday,
|
||||||
|
publishDate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Markdown(
|
||||||
|
markdown,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Tag(icon: ImageVector, text: String) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(16.dp),
|
||||||
|
tint = MaterialTheme.colorScheme.outline,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.outline,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ sealed interface SettingsDestination : Parcelable {
|
||||||
object About : SettingsDestination
|
object About : SettingsDestination
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object UpdateProgress : SettingsDestination
|
data class Update(val downloadOnScreenEntry: Boolean) : SettingsDestination
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object Changelogs : SettingsDestination
|
object Changelogs : SettingsDestination
|
||||||
|
@ -37,5 +37,4 @@ sealed interface SettingsDestination : Parcelable {
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object Licenses: SettingsDestination
|
object Licenses: SettingsDestination
|
||||||
|
|
||||||
}
|
}
|
|
@ -28,15 +28,17 @@ import androidx.compose.ui.res.stringResource
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.ui.component.AppTopBar
|
import app.revanced.manager.ui.component.AppTopBar
|
||||||
import app.revanced.manager.ui.component.NotificationCard
|
import app.revanced.manager.ui.component.NotificationCard
|
||||||
|
import app.revanced.manager.ui.component.settings.SettingsListItem
|
||||||
import app.revanced.manager.ui.destination.SettingsDestination
|
import app.revanced.manager.ui.destination.SettingsDestination
|
||||||
import app.revanced.manager.ui.screen.settings.*
|
import app.revanced.manager.ui.screen.settings.*
|
||||||
import app.revanced.manager.ui.screen.settings.update.ChangelogsScreen
|
import app.revanced.manager.ui.screen.settings.update.ChangelogsScreen
|
||||||
import app.revanced.manager.ui.screen.settings.update.UpdateProgressScreen
|
import app.revanced.manager.ui.screen.settings.update.UpdateScreen
|
||||||
import app.revanced.manager.ui.screen.settings.update.UpdatesSettingsScreen
|
import app.revanced.manager.ui.screen.settings.update.UpdatesSettingsScreen
|
||||||
import app.revanced.manager.ui.viewmodel.SettingsViewModel
|
import app.revanced.manager.ui.viewmodel.SettingsViewModel
|
||||||
import dev.olshevski.navigation.reimagined.*
|
import dev.olshevski.navigation.reimagined.*
|
||||||
import org.koin.androidx.compose.getViewModel
|
import org.koin.androidx.compose.getViewModel
|
||||||
import app.revanced.manager.ui.component.settings.SettingsListItem
|
import org.koin.core.parameter.parametersOf
|
||||||
|
import org.koin.androidx.compose.getViewModel as getComposeViewModel
|
||||||
|
|
||||||
@SuppressLint("BatteryLife")
|
@SuppressLint("BatteryLife")
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@ -96,7 +98,6 @@ fun SettingsScreen(
|
||||||
controller = navController
|
controller = navController
|
||||||
) { destination ->
|
) { destination ->
|
||||||
when (destination) {
|
when (destination) {
|
||||||
|
|
||||||
is SettingsDestination.General -> GeneralSettingsScreen(
|
is SettingsDestination.General -> GeneralSettingsScreen(
|
||||||
onBackClick = backClick,
|
onBackClick = backClick,
|
||||||
viewModel = viewModel
|
viewModel = viewModel
|
||||||
|
@ -109,7 +110,7 @@ fun SettingsScreen(
|
||||||
is SettingsDestination.Updates -> UpdatesSettingsScreen(
|
is SettingsDestination.Updates -> UpdatesSettingsScreen(
|
||||||
onBackClick = backClick,
|
onBackClick = backClick,
|
||||||
onChangelogClick = { navController.navigate(SettingsDestination.Changelogs) },
|
onChangelogClick = { navController.navigate(SettingsDestination.Changelogs) },
|
||||||
onUpdateClick = { navController.navigate(SettingsDestination.UpdateProgress) }
|
onUpdateClick = { navController.navigate(SettingsDestination.Update(false)) }
|
||||||
)
|
)
|
||||||
|
|
||||||
is SettingsDestination.Downloads -> DownloadsSettingsScreen(
|
is SettingsDestination.Downloads -> DownloadsSettingsScreen(
|
||||||
|
@ -126,8 +127,13 @@ fun SettingsScreen(
|
||||||
onLicensesClick = { navController.navigate(SettingsDestination.Licenses) }
|
onLicensesClick = { navController.navigate(SettingsDestination.Licenses) }
|
||||||
)
|
)
|
||||||
|
|
||||||
is SettingsDestination.UpdateProgress -> UpdateProgressScreen(
|
is SettingsDestination.Update -> UpdateScreen(
|
||||||
onBackClick = backClick,
|
onBackClick = backClick,
|
||||||
|
vm = getComposeViewModel {
|
||||||
|
parametersOf(
|
||||||
|
destination.downloadOnScreenEntry
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
is SettingsDestination.Changelogs -> ChangelogsScreen(
|
is SettingsDestination.Changelogs -> ChangelogsScreen(
|
||||||
|
|
|
@ -1,37 +1,27 @@
|
||||||
package app.revanced.manager.ui.screen.settings.update
|
package app.revanced.manager.ui.screen.settings.update
|
||||||
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
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.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
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.material3.Divider
|
import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.ui.component.AppTopBar
|
import app.revanced.manager.ui.component.AppTopBar
|
||||||
import app.revanced.manager.ui.component.LoadingIndicator
|
import app.revanced.manager.ui.component.LoadingIndicator
|
||||||
import app.revanced.manager.ui.component.Markdown
|
import app.revanced.manager.ui.component.settings.Changelog
|
||||||
import app.revanced.manager.ui.viewmodel.ChangelogsViewModel
|
import app.revanced.manager.ui.viewmodel.ChangelogsViewModel
|
||||||
import app.revanced.manager.util.formatNumber
|
import app.revanced.manager.util.formatNumber
|
||||||
import app.revanced.manager.util.relativeTime
|
import app.revanced.manager.util.relativeTime
|
||||||
|
@ -104,75 +94,3 @@ fun ChangelogItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun Changelog(
|
|
||||||
markdown: String,
|
|
||||||
version: String,
|
|
||||||
downloadCount: String,
|
|
||||||
publishDate: String
|
|
||||||
) {
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(bottom = 0.dp),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Outlined.Campaign,
|
|
||||||
contentDescription = null,
|
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
|
||||||
modifier = Modifier
|
|
||||||
.size(32.dp)
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
version.removePrefix("v"),
|
|
||||||
style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight(800)),
|
|
||||||
color = MaterialTheme.colorScheme.primary,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Tag(
|
|
||||||
Icons.Outlined.Sell,
|
|
||||||
version
|
|
||||||
)
|
|
||||||
Tag(
|
|
||||||
Icons.Outlined.FileDownload,
|
|
||||||
downloadCount
|
|
||||||
)
|
|
||||||
Tag(
|
|
||||||
Icons.Outlined.CalendarToday,
|
|
||||||
publishDate
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Markdown(
|
|
||||||
markdown,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun Tag(icon: ImageVector, text: String) {
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = icon,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(16.dp),
|
|
||||||
tint = MaterialTheme.colorScheme.outline,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text,
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = MaterialTheme.colorScheme.outline,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
package app.revanced.manager.ui.screen.settings.update
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
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.material3.Button
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.Stable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import app.revanced.manager.R
|
|
||||||
import app.revanced.manager.ui.component.AppTopBar
|
|
||||||
import app.revanced.manager.ui.viewmodel.UpdateProgressViewModel
|
|
||||||
import org.koin.androidx.compose.getViewModel
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
@Stable
|
|
||||||
fun UpdateProgressScreen(
|
|
||||||
onBackClick: () -> Unit,
|
|
||||||
vm: UpdateProgressViewModel = getViewModel()
|
|
||||||
) {
|
|
||||||
Scaffold(
|
|
||||||
topBar = {
|
|
||||||
AppTopBar(
|
|
||||||
title = stringResource(R.string.updates),
|
|
||||||
onBackClick = onBackClick
|
|
||||||
)
|
|
||||||
}
|
|
||||||
) { paddingValues ->
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(paddingValues)
|
|
||||||
.padding(vertical = 16.dp, horizontal = 24.dp)
|
|
||||||
.verticalScroll(rememberScrollState()),
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = if (vm.isInstalling) stringResource(R.string.installing_manager_update) else stringResource(
|
|
||||||
R.string.downloading_manager_update
|
|
||||||
), style = MaterialTheme.typography.headlineMedium
|
|
||||||
)
|
|
||||||
LinearProgressIndicator(
|
|
||||||
progress = vm.downloadProgress,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(vertical = 16.dp)
|
|
||||||
.fillMaxWidth()
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = if (!vm.isInstalling) "${vm.downloadedSize.div(1000000)} MB / ${
|
|
||||||
vm.totalSize.div(
|
|
||||||
1000000
|
|
||||||
)
|
|
||||||
} MB (${vm.downloadProgress.times(100).toInt()}%)" else stringResource(R.string.installing_message),
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = MaterialTheme.colorScheme.outline,
|
|
||||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
|
||||||
textAlign = TextAlign.Center
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "This update adds many functionality and fixes many issues in Manager. New experiment toggles are also added, they can be found in Settings > Advanced. Please submit some feedback in Settings > About > Submit issues or feedback. Thank you, everyone!",
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
modifier = Modifier.padding(vertical = 32.dp),
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.Bottom,
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
TextButton(
|
|
||||||
onClick = onBackClick,
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(R.string.cancel))
|
|
||||||
}
|
|
||||||
Button(onClick = vm::installUpdate, enabled = vm.finished) {
|
|
||||||
Text(text = stringResource(R.string.update))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,239 @@
|
||||||
|
package app.revanced.manager.ui.screen.settings.update
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
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.Update
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Divider
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
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.ui.component.AppTopBar
|
||||||
|
import app.revanced.manager.ui.component.settings.Changelog
|
||||||
|
import app.revanced.manager.ui.viewmodel.UpdateViewModel
|
||||||
|
import app.revanced.manager.ui.viewmodel.UpdateViewModel.Changelog
|
||||||
|
import app.revanced.manager.ui.viewmodel.UpdateViewModel.State
|
||||||
|
import app.revanced.manager.util.formatNumber
|
||||||
|
import app.revanced.manager.util.relativeTime
|
||||||
|
import com.gigamole.composefadingedges.content.FadingEdgesContentType
|
||||||
|
import com.gigamole.composefadingedges.content.scrollconfig.FadingEdgesScrollConfig
|
||||||
|
import com.gigamole.composefadingedges.fill.FadingEdgesFillType
|
||||||
|
import com.gigamole.composefadingedges.verticalFadingEdges
|
||||||
|
import org.koin.androidx.compose.getViewModel
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
@Stable
|
||||||
|
fun UpdateScreen(
|
||||||
|
onBackClick: () -> Unit,
|
||||||
|
vm: UpdateViewModel = getViewModel()
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
AppTopBar(
|
||||||
|
title = stringResource(R.string.updates),
|
||||||
|
onBackClick = onBackClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { paddingValues ->
|
||||||
|
AnimatedVisibility(visible = vm.showInternetCheckDialog) {
|
||||||
|
MeteredDownloadConfirmationDialog(
|
||||||
|
onDismiss = { vm.showInternetCheckDialog = false },
|
||||||
|
onDownloadAnyways = { vm.downloadUpdate(true) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(paddingValues)
|
||||||
|
.padding(vertical = 16.dp, horizontal = 24.dp)
|
||||||
|
.verticalScroll(rememberScrollState()),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(32.dp)
|
||||||
|
) {
|
||||||
|
Header(
|
||||||
|
vm.state,
|
||||||
|
vm.changelog,
|
||||||
|
DownloadData(vm.downloadProgress, vm.downloadedSize, vm.totalSize)
|
||||||
|
)
|
||||||
|
vm.changelog?.let { changelog ->
|
||||||
|
Divider()
|
||||||
|
Changelog(changelog)
|
||||||
|
} ?: Spacer(modifier = Modifier.weight(1f))
|
||||||
|
Buttons(vm.state, vm::downloadUpdate, vm::installUpdate, onBackClick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MeteredDownloadConfirmationDialog(
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onDownloadAnyways: () -> Unit
|
||||||
|
) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onDismiss) {
|
||||||
|
Text(stringResource(R.string.cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onDismiss()
|
||||||
|
onDownloadAnyways()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.download))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title = { Text(stringResource(R.string.download_update_confirmation)) },
|
||||||
|
icon = { Icon(Icons.Outlined.Update, null) },
|
||||||
|
text = { Text(stringResource(R.string.download_confirmation_metered)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Header(state: State, changelog: Changelog?, 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
|
||||||
|
)
|
||||||
|
changelog?.let { changelog ->
|
||||||
|
Text(
|
||||||
|
text = stringResource(
|
||||||
|
id = R.string.new_version,
|
||||||
|
changelog.version.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(changelog: Changelog) {
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.verticalScroll(scrollState)
|
||||||
|
.verticalFadingEdges(
|
||||||
|
fillType = FadingEdgesFillType.FadeColor(
|
||||||
|
color = MaterialTheme.colorScheme.background,
|
||||||
|
fillStops = Triple(0F, 0.55F, 1F),
|
||||||
|
secondStopAlpha = 1F
|
||||||
|
),
|
||||||
|
contentType = FadingEdgesContentType.Dynamic.Scroll(
|
||||||
|
state = scrollState,
|
||||||
|
scrollConfig = FadingEdgesScrollConfig.Dynamic(
|
||||||
|
animationSpec = spring(),
|
||||||
|
isLerpByDifferenceForPartialContent = true,
|
||||||
|
scrollFactor = 1.25F
|
||||||
|
)
|
||||||
|
),
|
||||||
|
length = 350.dp
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Changelog(
|
||||||
|
markdown = changelog.body.replace("`", ""),
|
||||||
|
version = changelog.version,
|
||||||
|
downloadCount = changelog.downloadCount.formatNumber(),
|
||||||
|
publishDate = changelog.publishDate.relativeTime(LocalContext.current)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
)
|
|
@ -1,75 +0,0 @@
|
||||||
package app.revanced.manager.ui.viewmodel
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import app.revanced.manager.R
|
|
||||||
import app.revanced.manager.network.api.ReVancedAPI
|
|
||||||
import app.revanced.manager.network.api.ReVancedAPI.Extensions.findAssetByType
|
|
||||||
import app.revanced.manager.network.service.HttpService
|
|
||||||
import app.revanced.manager.network.utils.getOrThrow
|
|
||||||
import app.revanced.manager.util.APK_MIMETYPE
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import app.revanced.manager.util.PM
|
|
||||||
import app.revanced.manager.util.uiSafe
|
|
||||||
import io.ktor.client.plugins.onDownload
|
|
||||||
import io.ktor.client.request.url
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class UpdateProgressViewModel(
|
|
||||||
app: Application,
|
|
||||||
private val reVancedAPI: ReVancedAPI,
|
|
||||||
private val http: HttpService,
|
|
||||||
private val pm: PM
|
|
||||||
) : ViewModel() {
|
|
||||||
var downloadedSize by mutableStateOf(0L)
|
|
||||||
private set
|
|
||||||
var totalSize by mutableStateOf(0L)
|
|
||||||
private set
|
|
||||||
val downloadProgress by derivedStateOf {
|
|
||||||
if (downloadedSize == 0L || totalSize == 0L) return@derivedStateOf 0f
|
|
||||||
|
|
||||||
downloadedSize.toFloat() / totalSize.toFloat()
|
|
||||||
}
|
|
||||||
val isInstalling by derivedStateOf { downloadProgress >= 1 }
|
|
||||||
var finished by mutableStateOf(false)
|
|
||||||
private set
|
|
||||||
|
|
||||||
private val location = File.createTempFile("updater", ".apk", app.cacheDir)
|
|
||||||
private val job = viewModelScope.launch {
|
|
||||||
uiSafe(app, R.string.download_manager_failed, "Failed to download ReVanced Manager") {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
val asset = reVancedAPI
|
|
||||||
.getLatestRelease("revanced-manager")
|
|
||||||
.getOrThrow()
|
|
||||||
.findAssetByType(APK_MIMETYPE)
|
|
||||||
|
|
||||||
http.download(location) {
|
|
||||||
url(asset.downloadUrl)
|
|
||||||
onDownload { bytesSentTotal, contentLength ->
|
|
||||||
downloadedSize = bytesSentTotal
|
|
||||||
totalSize = contentLength
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finished = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun installUpdate() = viewModelScope.launch {
|
|
||||||
pm.installApp(listOf(location))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCleared() {
|
|
||||||
super.onCleared()
|
|
||||||
|
|
||||||
job.cancel()
|
|
||||||
location.delete()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
package app.revanced.manager.ui.viewmodel
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.content.pm.PackageInstaller
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import app.revanced.manager.R
|
||||||
|
import app.revanced.manager.data.platform.NetworkInfo
|
||||||
|
import app.revanced.manager.network.api.ReVancedAPI
|
||||||
|
import app.revanced.manager.network.api.ReVancedAPI.Extensions.findAssetByType
|
||||||
|
import app.revanced.manager.network.dto.ReVancedRelease
|
||||||
|
import app.revanced.manager.network.service.HttpService
|
||||||
|
import app.revanced.manager.network.utils.getOrThrow
|
||||||
|
import app.revanced.manager.service.InstallService
|
||||||
|
import app.revanced.manager.service.UninstallService
|
||||||
|
import app.revanced.manager.util.APK_MIMETYPE
|
||||||
|
import app.revanced.manager.util.PM
|
||||||
|
import app.revanced.manager.util.simpleMessage
|
||||||
|
import app.revanced.manager.util.tag
|
||||||
|
import app.revanced.manager.util.toast
|
||||||
|
import app.revanced.manager.util.uiSafe
|
||||||
|
import io.ktor.client.plugins.onDownload
|
||||||
|
import io.ktor.client.request.url
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.inject
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class UpdateViewModel(
|
||||||
|
private val downloadOnScreenEntry: Boolean
|
||||||
|
) : ViewModel(), KoinComponent {
|
||||||
|
private val app: Application by inject()
|
||||||
|
private val reVancedAPI: ReVancedAPI by inject()
|
||||||
|
private val http: HttpService by inject()
|
||||||
|
private val pm: PM by inject()
|
||||||
|
private val networkInfo: NetworkInfo by inject()
|
||||||
|
|
||||||
|
var downloadedSize by mutableStateOf(0L)
|
||||||
|
private set
|
||||||
|
var totalSize by mutableStateOf(0L)
|
||||||
|
private set
|
||||||
|
val downloadProgress by derivedStateOf {
|
||||||
|
if (downloadedSize == 0L || totalSize == 0L) return@derivedStateOf 0f
|
||||||
|
|
||||||
|
downloadedSize.toFloat() / totalSize.toFloat()
|
||||||
|
}
|
||||||
|
var showInternetCheckDialog by mutableStateOf(false)
|
||||||
|
var state by mutableStateOf(State.CAN_DOWNLOAD)
|
||||||
|
private set
|
||||||
|
|
||||||
|
var installError by mutableStateOf("")
|
||||||
|
|
||||||
|
var changelog: Changelog? by mutableStateOf(null)
|
||||||
|
|
||||||
|
private val location = File.createTempFile("updater", ".apk", app.cacheDir)
|
||||||
|
private var release: ReVancedRelease? = null
|
||||||
|
private val job = viewModelScope.launch {
|
||||||
|
uiSafe(app, R.string.download_manager_failed, "Failed to download ReVanced Manager") {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val response = reVancedAPI
|
||||||
|
.getLatestRelease("revanced-manager")
|
||||||
|
.getOrThrow()
|
||||||
|
release = response
|
||||||
|
changelog = Changelog(
|
||||||
|
response.metadata.tag,
|
||||||
|
response.findAssetByType(APK_MIMETYPE).downloadCount,
|
||||||
|
response.metadata.publishedAt,
|
||||||
|
response.metadata.body
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (downloadOnScreenEntry) {
|
||||||
|
downloadUpdate()
|
||||||
|
} else {
|
||||||
|
state = State.CAN_DOWNLOAD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun downloadUpdate(ignoreInternetCheck: Boolean = false) = viewModelScope.launch {
|
||||||
|
uiSafe(app, R.string.failed_to_download_update, "Failed to download update") {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
if (!networkInfo.isSafe() && !ignoreInternetCheck) {
|
||||||
|
showInternetCheckDialog = true
|
||||||
|
} else {
|
||||||
|
state = State.DOWNLOADING
|
||||||
|
val asset = release?.findAssetByType(APK_MIMETYPE)
|
||||||
|
?: throw Exception("couldn't find asset to download")
|
||||||
|
|
||||||
|
http.download(location) {
|
||||||
|
url(asset.downloadUrl)
|
||||||
|
onDownload { bytesSentTotal, contentLength ->
|
||||||
|
downloadedSize = bytesSentTotal
|
||||||
|
totalSize = contentLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state = State.CAN_INSTALL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun installUpdate() = viewModelScope.launch {
|
||||||
|
state = State.INSTALLING
|
||||||
|
|
||||||
|
pm.installApp(listOf(location))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val installBroadcastReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
|
intent?.let {
|
||||||
|
val pmStatus = intent.getIntExtra(InstallService.EXTRA_INSTALL_STATUS, -999)
|
||||||
|
val extra =
|
||||||
|
intent.getStringExtra(InstallService.EXTRA_INSTALL_STATUS_MESSAGE)!!
|
||||||
|
|
||||||
|
if (pmStatus == PackageInstaller.STATUS_SUCCESS) {
|
||||||
|
app.toast(app.getString(R.string.install_app_success))
|
||||||
|
state = State.SUCCESS
|
||||||
|
} else {
|
||||||
|
state = State.FAILED
|
||||||
|
// TODO: handle install fail with a popup
|
||||||
|
installError = extra
|
||||||
|
app.toast(app.getString(R.string.install_app_fail, extra))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
ContextCompat.registerReceiver(app, installBroadcastReceiver, IntentFilter().apply {
|
||||||
|
addAction(InstallService.APP_INSTALL_ACTION)
|
||||||
|
}, ContextCompat.RECEIVER_NOT_EXPORTED)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
super.onCleared()
|
||||||
|
app.unregisterReceiver(installBroadcastReceiver)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
location.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Changelog(
|
||||||
|
val version: String,
|
||||||
|
val downloadCount: Int,
|
||||||
|
val publishDate: String,
|
||||||
|
val body: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
enum class State(@StringRes val title: Int, val showCancel: Boolean = false) {
|
||||||
|
CAN_DOWNLOAD(R.string.update_available),
|
||||||
|
DOWNLOADING(R.string.downloading_manager_update, true),
|
||||||
|
CAN_INSTALL(R.string.ready_to_install_update, true),
|
||||||
|
INSTALLING(R.string.installing_manager_update),
|
||||||
|
FAILED(R.string.install_update_manager_failed),
|
||||||
|
SUCCESS(R.string.update_completed)
|
||||||
|
}
|
||||||
|
}
|
|
@ -273,6 +273,12 @@
|
||||||
<string name="bundle_type_description">Choose the type of bundle you want</string>
|
<string name="bundle_type_description">Choose the type of bundle you want</string>
|
||||||
<string name="about_revanced_manager">About ReVanced Manager</string>
|
<string name="about_revanced_manager">About ReVanced Manager</string>
|
||||||
<string name="revanced_manager_description">ReVanced Manager is an application designed to work with ReVanced Patcher, which allows for long-lasting patches to be created for Android apps. The patching system is designed to automatically work with new versions of apps with minimal maintenance.</string>
|
<string name="revanced_manager_description">ReVanced Manager is an application designed to work with ReVanced Patcher, which allows for long-lasting patches to be created for Android apps. The patching system is designed to automatically work with new versions of apps with minimal maintenance.</string>
|
||||||
|
<string name="update_available">An update is available</string>
|
||||||
|
<string name="current_version">Current version: %s</string>
|
||||||
|
<string name="new_version">New version: %s</string>
|
||||||
|
<string name="ready_to_install_update">Ready to install update</string>
|
||||||
|
<string name="update_completed">Update installed</string>
|
||||||
|
<string name="install_update_manager_failed">Failed to install update</string>
|
||||||
<string name="update_notification">A minor update for ReVanced Manager is available. Click here to update and get the latest features and fixes!</string>
|
<string name="update_notification">A minor update for ReVanced Manager is available. Click here to update and get the latest features and fixes!</string>
|
||||||
<string name="update_channel">Update channel</string>
|
<string name="update_channel">Update channel</string>
|
||||||
<string name="update_channel_description">Stable</string>
|
<string name="update_channel_description">Stable</string>
|
||||||
|
@ -300,7 +306,11 @@
|
||||||
|
|
||||||
<string name="failed_to_check_updates">Failed to check for updates</string>
|
<string name="failed_to_check_updates">Failed to check for updates</string>
|
||||||
<string name="dismiss_temporary">Not now</string>
|
<string name="dismiss_temporary">Not now</string>
|
||||||
<string name="update_available">New update available</string>
|
<string name="update_available_dialog_title">New update available</string>
|
||||||
<string name="update_available_description">A new version (%s) is available for download.</string>
|
<string name="update_available_dialog_description">A new version (%s) is available for download.</string>
|
||||||
|
<string name="failed_to_download_update">Failed to download update: %s</string>
|
||||||
|
<string name="download">Download</string>
|
||||||
|
<string name="download_confirmation_metered">You are currently on a metered connection, and data charges from your service provider may apply.\n\nDo you still want to continue?</string>
|
||||||
|
<string name="download_update_confirmation">Download update?</string>
|
||||||
<string name="no_contributors_found">No contributors found</string>
|
<string name="no_contributors_found">No contributors found</string>
|
||||||
</resources>
|
</resources>
|
|
@ -18,6 +18,7 @@ koin-version-compose = "3.4.6"
|
||||||
reimagined-navigation = "1.5.0"
|
reimagined-navigation = "1.5.0"
|
||||||
ktor = "2.3.3"
|
ktor = "2.3.3"
|
||||||
markdown-renderer = "0.8.0"
|
markdown-renderer = "0.8.0"
|
||||||
|
fading-edges = "1.0.4"
|
||||||
androidGradlePlugin = "8.1.2"
|
androidGradlePlugin = "8.1.2"
|
||||||
kotlinGradlePlugin = "1.9.10"
|
kotlinGradlePlugin = "1.9.10"
|
||||||
devToolsGradlePlugin = "1.9.10-1.0.13"
|
devToolsGradlePlugin = "1.9.10-1.0.13"
|
||||||
|
@ -95,6 +96,9 @@ skrapeit-parser = { group = "it.skrape", name = "skrapeit-html-parser", version.
|
||||||
# Markdown
|
# Markdown
|
||||||
markdown-renderer = { group = "com.mikepenz", name = "multiplatform-markdown-renderer-android", version.ref = "markdown-renderer" }
|
markdown-renderer = { group = "com.mikepenz", name = "multiplatform-markdown-renderer-android", version.ref = "markdown-renderer" }
|
||||||
|
|
||||||
|
# Fading Edges
|
||||||
|
fading-edges = { group = "com.github.GIGAMOLE", name = "ComposeFadingEdges", version.ref = "fading-edges"}
|
||||||
|
|
||||||
# LibSU
|
# LibSU
|
||||||
libsu-core = { group = "com.github.topjohnwu.libsu", name = "core", version.ref = "libsu" }
|
libsu-core = { group = "com.github.topjohnwu.libsu", name = "core", version.ref = "libsu" }
|
||||||
libsu-service = { group = "com.github.topjohnwu.libsu", name = "service", version.ref = "libsu" }
|
libsu-service = { group = "com.github.topjohnwu.libsu", name = "service", version.ref = "libsu" }
|
||||||
|
|
Loading…
Reference in a new issue