mirror of
https://github.com/revanced/revanced-manager
synced 2024-05-14 13:56:57 +02:00
feat: in-app updater (#25)
This commit is contained in:
parent
c332760786
commit
a4842c078b
@ -4,6 +4,7 @@ import app.revanced.manager.compose.ui.viewmodel.AppSelectorViewModel
|
|||||||
import app.revanced.manager.compose.ui.viewmodel.InstallerScreenViewModel
|
import app.revanced.manager.compose.ui.viewmodel.InstallerScreenViewModel
|
||||||
import app.revanced.manager.compose.ui.viewmodel.PatchesSelectorViewModel
|
import app.revanced.manager.compose.ui.viewmodel.PatchesSelectorViewModel
|
||||||
import app.revanced.manager.compose.ui.viewmodel.SettingsViewModel
|
import app.revanced.manager.compose.ui.viewmodel.SettingsViewModel
|
||||||
|
import app.revanced.manager.compose.ui.viewmodel.UpdateSettingsViewModel
|
||||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||||
import org.koin.androidx.viewmodel.dsl.viewModelOf
|
import org.koin.androidx.viewmodel.dsl.viewModelOf
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
@ -25,4 +26,5 @@ val viewModelModule = module {
|
|||||||
signerService = get(),
|
signerService = get(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
viewModelOf(::UpdateSettingsViewModel)
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
package app.revanced.manager.compose.network.api
|
package app.revanced.manager.compose.network.api
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.os.Environment
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import app.revanced.manager.compose.domain.repository.ReVancedRepository
|
import app.revanced.manager.compose.domain.repository.ReVancedRepository
|
||||||
import app.revanced.manager.compose.util.ghIntegrations
|
import app.revanced.manager.compose.util.ghIntegrations
|
||||||
|
import app.revanced.manager.compose.util.ghManager
|
||||||
import app.revanced.manager.compose.util.ghPatches
|
import app.revanced.manager.compose.util.ghPatches
|
||||||
import app.revanced.manager.compose.util.tag
|
import app.revanced.manager.compose.util.tag
|
||||||
import app.revanced.manager.compose.util.toast
|
import app.revanced.manager.compose.util.toast
|
||||||
@ -24,11 +26,15 @@ class ManagerAPI(
|
|||||||
private val revancedRepository: ReVancedRepository
|
private val revancedRepository: ReVancedRepository
|
||||||
) {
|
) {
|
||||||
var downloadProgress: Float? by mutableStateOf(null)
|
var downloadProgress: Float? by mutableStateOf(null)
|
||||||
|
var downloadedSize: Long? by mutableStateOf(null)
|
||||||
|
var totalSize: Long? by mutableStateOf(null)
|
||||||
|
|
||||||
private suspend fun downloadAsset(downloadUrl: String, saveLocation: File) {
|
private suspend fun downloadAsset(downloadUrl: String, saveLocation: File) {
|
||||||
client.get(downloadUrl) {
|
client.get(downloadUrl) {
|
||||||
onDownload { bytesSentTotal, contentLength ->
|
onDownload { bytesSentTotal, contentLength, ->
|
||||||
downloadProgress = (bytesSentTotal.toFloat() / contentLength.toFloat())
|
downloadProgress = (bytesSentTotal.toFloat() / contentLength.toFloat())
|
||||||
|
downloadedSize = bytesSentTotal
|
||||||
|
totalSize = contentLength
|
||||||
}
|
}
|
||||||
}.bodyAsChannel().copyAndClose(saveLocation.writeChannel())
|
}.bodyAsChannel().copyAndClose(saveLocation.writeChannel())
|
||||||
downloadProgress = null
|
downloadProgress = null
|
||||||
@ -65,10 +71,20 @@ class ManagerAPI(
|
|||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun downloadManager(): File? {
|
||||||
|
try {
|
||||||
|
val managerAsset = revancedRepository.findAsset(ghManager, ".apk")
|
||||||
|
val managerFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).also { it.mkdirs() }
|
||||||
|
.resolve("revanced-manager.apk")
|
||||||
|
downloadAsset(managerAsset.downloadUrl, managerFile)
|
||||||
|
println("Downloaded manager at ${managerFile.absolutePath}")
|
||||||
|
return managerFile
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(tag, "Failed to download manager", e)
|
||||||
|
app.toast("Failed to download manager")
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PatchesAsset(
|
|
||||||
val downloadUrl: String, val name: String
|
|
||||||
)
|
|
||||||
|
|
||||||
class MissingAssetException : Exception()
|
class MissingAssetException : Exception()
|
@ -1,7 +1,7 @@
|
|||||||
package app.revanced.manager.compose.network.service
|
package app.revanced.manager.compose.network.service
|
||||||
|
|
||||||
import app.revanced.manager.compose.network.api.MissingAssetException
|
import app.revanced.manager.compose.network.api.MissingAssetException
|
||||||
import app.revanced.manager.compose.network.api.PatchesAsset
|
import app.revanced.manager.compose.network.dto.Assets
|
||||||
import app.revanced.manager.compose.network.dto.ReVancedReleases
|
import app.revanced.manager.compose.network.dto.ReVancedReleases
|
||||||
import app.revanced.manager.compose.network.dto.ReVancedRepositories
|
import app.revanced.manager.compose.network.dto.ReVancedRepositories
|
||||||
import app.revanced.manager.compose.network.utils.APIResponse
|
import app.revanced.manager.compose.network.utils.APIResponse
|
||||||
@ -30,12 +30,12 @@ class ReVancedService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findAsset(repo: String, file: String): PatchesAsset {
|
suspend fun findAsset(repo: String, file: String): Assets {
|
||||||
val releases = getAssets().getOrNull() ?: throw Exception("Cannot retrieve assets")
|
val releases = getAssets().getOrNull() ?: throw Exception("Cannot retrieve assets")
|
||||||
val asset = releases.tools.find { asset ->
|
val asset = releases.tools.find { asset ->
|
||||||
(asset.name.contains(file) && asset.repository.contains(repo))
|
(asset.name.contains(file) && asset.repository.contains(repo))
|
||||||
} ?: throw MissingAssetException()
|
} ?: throw MissingAssetException()
|
||||||
return PatchesAsset(asset.downloadUrl, asset.name)
|
return Assets(asset.repository, asset.version, asset.timestamp, asset.name,asset.size, asset.downloadUrl, asset.content_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
@ -23,4 +23,7 @@ sealed interface SettingsDestination : Parcelable {
|
|||||||
@Parcelize
|
@Parcelize
|
||||||
object About : SettingsDestination
|
object About : SettingsDestination
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
object UpdateProgress : SettingsDestination
|
||||||
|
|
||||||
}
|
}
|
@ -41,6 +41,7 @@ import app.revanced.manager.compose.ui.component.AppTopBar
|
|||||||
import app.revanced.manager.compose.ui.destination.SettingsDestination
|
import app.revanced.manager.compose.ui.destination.SettingsDestination
|
||||||
import app.revanced.manager.compose.ui.screen.settings.*
|
import app.revanced.manager.compose.ui.screen.settings.*
|
||||||
import app.revanced.manager.compose.ui.viewmodel.SettingsViewModel
|
import app.revanced.manager.compose.ui.viewmodel.SettingsViewModel
|
||||||
|
import app.revanced.manager.compose.ui.viewmodel.UpdateSettingsViewModel
|
||||||
import dev.olshevski.navigation.reimagined.*
|
import dev.olshevski.navigation.reimagined.*
|
||||||
import org.koin.androidx.compose.getViewModel
|
import org.koin.androidx.compose.getViewModel
|
||||||
|
|
||||||
@ -99,7 +100,8 @@ fun SettingsScreen(
|
|||||||
)
|
)
|
||||||
|
|
||||||
is SettingsDestination.Updates -> UpdatesSettingsScreen(
|
is SettingsDestination.Updates -> UpdatesSettingsScreen(
|
||||||
onBackClick = { navController.pop() }
|
onBackClick = { navController.pop() },
|
||||||
|
navController = navController
|
||||||
)
|
)
|
||||||
|
|
||||||
is SettingsDestination.Downloads -> DownloadsSettingsScreen(
|
is SettingsDestination.Downloads -> DownloadsSettingsScreen(
|
||||||
@ -114,6 +116,10 @@ fun SettingsScreen(
|
|||||||
onBackClick = { navController.pop() }
|
onBackClick = { navController.pop() }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
is SettingsDestination.UpdateProgress -> UpdateProgressScreen(
|
||||||
|
{ navController.pop() },
|
||||||
|
)
|
||||||
|
|
||||||
is SettingsDestination.Settings -> {
|
is SettingsDestination.Settings -> {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
@ -136,7 +142,8 @@ fun SettingsScreen(
|
|||||||
context.startActivity(Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
|
context.startActivity(Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
|
||||||
data = Uri.parse("package:${context.packageName}")
|
data = Uri.parse("package:${context.packageName}")
|
||||||
})
|
})
|
||||||
showBatteryButton = !pm.isIgnoringBatteryOptimizations(context.packageName)
|
showBatteryButton =
|
||||||
|
!pm.isIgnoringBatteryOptimizations(context.packageName)
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@ -151,16 +158,36 @@ fun SettingsScreen(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
Icon(imageVector = Icons.Default.BatteryAlert, contentDescription = null, tint = MaterialTheme.colorScheme.onTertiaryContainer, modifier = Modifier.size(24.dp))
|
Icon(
|
||||||
Text(text = stringResource(R.string.battery_optimization_notification), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onTertiaryContainer)
|
imageVector = Icons.Default.BatteryAlert,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.onTertiaryContainer,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.battery_optimization_notification),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onTertiaryContainer
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
settingsSections.forEach { (titleDescIcon, destination) ->
|
settingsSections.forEach { (titleDescIcon, destination) ->
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier.clickable { navController.navigate(destination) },
|
modifier = Modifier.clickable { navController.navigate(destination) },
|
||||||
headlineContent = { Text(stringResource(titleDescIcon.first), style = MaterialTheme.typography.titleLarge) },
|
headlineContent = {
|
||||||
supportingContent = { Text(stringResource(titleDescIcon.second), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.outline) },
|
Text(
|
||||||
|
stringResource(titleDescIcon.first),
|
||||||
|
style = MaterialTheme.typography.titleLarge
|
||||||
|
)
|
||||||
|
},
|
||||||
|
supportingContent = {
|
||||||
|
Text(
|
||||||
|
stringResource(titleDescIcon.second),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.outline
|
||||||
|
)
|
||||||
|
},
|
||||||
leadingContent = { Icon(titleDescIcon.third, null) }
|
leadingContent = { Icon(titleDescIcon.third, null) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package app.revanced.manager.compose.ui.screen.settings
|
package app.revanced.manager.compose.ui.screen.settings
|
||||||
|
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
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
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@ -14,33 +16,57 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Update
|
import androidx.compose.material.icons.filled.Update
|
||||||
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
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.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import app.revanced.manager.compose.R
|
import app.revanced.manager.compose.R
|
||||||
import app.revanced.manager.compose.ui.component.AppTopBar
|
import app.revanced.manager.compose.ui.component.AppTopBar
|
||||||
|
import app.revanced.manager.compose.ui.destination.SettingsDestination
|
||||||
|
import app.revanced.manager.compose.ui.viewmodel.UpdateSettingsViewModel
|
||||||
|
import dev.olshevski.navigation.reimagined.NavController
|
||||||
|
import dev.olshevski.navigation.reimagined.navigate
|
||||||
|
import org.koin.androidx.compose.getViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun UpdatesSettingsScreen(
|
fun UpdatesSettingsScreen(
|
||||||
onBackClick: () -> Unit
|
onBackClick: () -> Unit,
|
||||||
|
navController: NavController<SettingsDestination>,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val listItems = listOf(
|
val listItems = listOf(
|
||||||
Triple(stringResource(R.string.update_channel), stringResource(R.string.update_channel_description), third = { /*TODO*/ }),
|
Triple(
|
||||||
Triple(stringResource(R.string.update_notifications), stringResource(R.string.update_notifications_description), third = { /*TODO*/ }),
|
stringResource(R.string.update_channel),
|
||||||
Triple(stringResource(R.string.changelog), stringResource(R.string.changelog_description), third = { /*TODO*/ }),
|
stringResource(R.string.update_channel_description),
|
||||||
|
third = { /*TODO*/ }),
|
||||||
|
Triple(
|
||||||
|
stringResource(R.string.update_notifications),
|
||||||
|
stringResource(R.string.update_notifications_description),
|
||||||
|
third = { /*TODO*/ }),
|
||||||
|
Triple(
|
||||||
|
stringResource(R.string.changelog),
|
||||||
|
stringResource(R.string.changelog_description),
|
||||||
|
third = { /*TODO*/ }),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
AppTopBar(
|
AppTopBar(
|
||||||
@ -55,15 +81,31 @@ fun UpdatesSettingsScreen(
|
|||||||
.padding(paddingValues)
|
.padding(paddingValues)
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
) {
|
) {
|
||||||
UpdateNotification()
|
UpdateNotification(
|
||||||
|
onClick = {
|
||||||
|
navController.navigate(SettingsDestination.UpdateProgress)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
listItems.forEach { (title, description, onClick) ->
|
listItems.forEach { (title, description, onClick) ->
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.clickable { onClick() },
|
.clickable { onClick() },
|
||||||
headlineContent = { Text(title, style = MaterialTheme.typography.titleLarge) },
|
headlineContent = {
|
||||||
supportingContent = { Text(description, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.outline) }
|
Text(
|
||||||
|
title,
|
||||||
|
style = MaterialTheme.typography.titleLarge
|
||||||
|
)
|
||||||
|
},
|
||||||
|
supportingContent = {
|
||||||
|
Text(
|
||||||
|
description,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.outline
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,13 +113,16 @@ fun UpdatesSettingsScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UpdateNotification() {
|
fun UpdateNotification(
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
.clip(RoundedCornerShape(24.dp))
|
.clip(RoundedCornerShape(24.dp))
|
||||||
.background(MaterialTheme.colorScheme.secondaryContainer)
|
.background(MaterialTheme.colorScheme.secondaryContainer)
|
||||||
|
.clickable { onClick() },
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -87,7 +132,79 @@ fun UpdateNotification() {
|
|||||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
Icon(imageVector = Icons.Default.Update, contentDescription = null)
|
Icon(imageVector = Icons.Default.Update, contentDescription = null)
|
||||||
Text(text = stringResource(R.string.update_notification), style = MaterialTheme.typography.bodyMedium)
|
Text(
|
||||||
|
text = stringResource(R.string.update_notification),
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
@Stable
|
||||||
|
fun UpdateProgressScreen(
|
||||||
|
onBackClick: () -> Unit,
|
||||||
|
vm: UpdateSettingsViewModel = 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),
|
||||||
|
) {
|
||||||
|
var isInstalling by remember { mutableStateOf(false) }
|
||||||
|
isInstalling = vm.downloadProgress >= 100
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = if (isInstalling) stringResource(R.string.installing_manager_update) else stringResource(
|
||||||
|
R.string.downloading_manager_update
|
||||||
|
), style = MaterialTheme.typography.headlineMedium
|
||||||
|
)
|
||||||
|
LinearProgressIndicator(
|
||||||
|
progress = vm.downloadProgress / 100f,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(vertical = 16.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = if (!isInstalling) "${vm.downloadedSize.div(1000000)} MB / ${vm.totalSize.div(1000000)} MB (${vm.downloadProgress.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 = { /*TODO*/ },
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(R.string.cancel))
|
||||||
|
}
|
||||||
|
Button(onClick = {
|
||||||
|
vm.installUpdate()
|
||||||
|
}) {
|
||||||
|
Text(text = stringResource(R.string.update))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package app.revanced.manager.compose.ui.viewmodel
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.os.Environment
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import app.revanced.manager.compose.network.api.ManagerAPI
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import app.revanced.manager.compose.util.PM
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class UpdateSettingsViewModel(
|
||||||
|
private val managerAPI: ManagerAPI,
|
||||||
|
private val app: Application,
|
||||||
|
) : ViewModel() {
|
||||||
|
val downloadProgress get() = (managerAPI.downloadProgress?.times(100)) ?: 0f
|
||||||
|
val downloadedSize get() = managerAPI.downloadedSize ?: 0L
|
||||||
|
val totalSize get() = managerAPI.totalSize ?: 0L
|
||||||
|
private fun downloadLatestManager() {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
managerAPI.downloadManager()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun installUpdate() {
|
||||||
|
PM.installApp(
|
||||||
|
apks = listOf(
|
||||||
|
File(
|
||||||
|
(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS + "/revanced-manager.apk")
|
||||||
|
.toString())
|
||||||
|
),
|
||||||
|
),
|
||||||
|
context = app,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
downloadLatestManager()
|
||||||
|
}
|
||||||
|
}
|
@ -92,4 +92,9 @@
|
|||||||
<string name="changelog">Changelog</string>
|
<string name="changelog">Changelog</string>
|
||||||
<string name="changelog_description">Check out the latest changes in this update</string>
|
<string name="changelog_description">Check out the latest changes in this update</string>
|
||||||
<string name="battery_optimization_notification">Battery optimization must be turned off in order for ReVanced Manager to work correctly in the background. Tap here to turn off.</string>
|
<string name="battery_optimization_notification">Battery optimization must be turned off in order for ReVanced Manager to work correctly in the background. Tap here to turn off.</string>
|
||||||
|
<string name="installing_manager_update">Installing update…</string>
|
||||||
|
<string name="downloading_manager_update">Downloading update…</string>
|
||||||
|
<string name="cancel">Cancel</string>
|
||||||
|
<string name="update">Update</string>
|
||||||
|
<string name="installing_message">Tap on <b>Update</b> when prompted. \n ReVanced Manager will close when updating.</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue
Block a user