mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2024-11-10 01:01:56 +01:00
feat: updater UI and code improvements (#1597)
This commit is contained in:
parent
aa6e612fba
commit
b7cb6b94f5
11 changed files with 102 additions and 61 deletions
|
@ -21,4 +21,5 @@ val viewModelModule = module {
|
|||
viewModelOf(::DownloadsViewModel)
|
||||
viewModelOf(::InstalledAppsViewModel)
|
||||
viewModelOf(::InstalledAppInfoViewModel)
|
||||
viewModelOf(::UpdatesSettingsViewModel)
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ class APIPatchBundle(name: String, id: Int, directory: File, endpoint: String) :
|
|||
.getLatestRelease(repo)
|
||||
.getOrThrow()
|
||||
.let {
|
||||
BundleAsset(it.metadata.tag, it.findAssetByType(mime).downloadUrl)
|
||||
BundleAsset(it.version, it.findAssetByType(mime).downloadUrl)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package app.revanced.manager.network.api
|
||||
|
||||
import android.os.Build
|
||||
import app.revanced.manager.domain.manager.PreferencesManager
|
||||
import app.revanced.manager.network.dto.ReVancedRelease
|
||||
import app.revanced.manager.network.service.ReVancedService
|
||||
import app.revanced.manager.network.utils.getOrThrow
|
||||
import app.revanced.manager.network.utils.transform
|
||||
|
||||
class ReVancedAPI(
|
||||
|
@ -13,12 +15,20 @@ class ReVancedAPI(
|
|||
|
||||
suspend fun getContributors() = service.getContributors(apiUrl()).transform { it.repositories }
|
||||
|
||||
suspend fun getLatestRelease(name: String) = service.getLatestRelease(apiUrl(), name).transform { it.release }
|
||||
suspend fun getLatestRelease(name: String) =
|
||||
service.getLatestRelease(apiUrl(), name).transform { it.release }
|
||||
|
||||
suspend fun getReleases(name: String) = service.getReleases(apiUrl(), name).transform { it.releases }
|
||||
suspend fun getReleases(name: String) =
|
||||
service.getReleases(apiUrl(), name).transform { it.releases }
|
||||
|
||||
suspend fun getAppUpdate() =
|
||||
getLatestRelease("revanced-manager")
|
||||
.getOrThrow()
|
||||
.takeIf { it.version != Build.VERSION.RELEASE }
|
||||
|
||||
companion object Extensions {
|
||||
fun ReVancedRelease.findAssetByType(mime: String) = assets.singleOrNull { it.contentType == mime } ?: throw MissingAssetException(mime)
|
||||
fun ReVancedRelease.findAssetByType(mime: String) =
|
||||
assets.singleOrNull { it.contentType == mime } ?: throw MissingAssetException(mime)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,9 @@ data class ReVancedReleases(
|
|||
data class ReVancedRelease(
|
||||
val metadata: ReVancedReleaseMeta,
|
||||
val assets: List<Asset>
|
||||
)
|
||||
) {
|
||||
val version get() = metadata.tag
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ReVancedReleaseMeta(
|
||||
|
|
|
@ -14,6 +14,7 @@ import kotlinx.coroutines.launch
|
|||
|
||||
@Composable
|
||||
fun BooleanItem(
|
||||
modifier: Modifier = Modifier,
|
||||
preference: Preference<Boolean>,
|
||||
coroutineScope: CoroutineScope = rememberCoroutineScope(),
|
||||
@StringRes headline: Int,
|
||||
|
@ -22,6 +23,7 @@ fun BooleanItem(
|
|||
val value by preference.getAsState()
|
||||
|
||||
BooleanItem(
|
||||
modifier = modifier,
|
||||
value = value,
|
||||
onValueChange = { coroutineScope.launch { preference.update(it) } },
|
||||
headline = headline,
|
||||
|
@ -31,12 +33,15 @@ fun BooleanItem(
|
|||
|
||||
@Composable
|
||||
fun BooleanItem(
|
||||
modifier: Modifier = Modifier,
|
||||
value: Boolean,
|
||||
onValueChange: (Boolean) -> Unit,
|
||||
@StringRes headline: Int,
|
||||
@StringRes description: Int
|
||||
) = SettingsListItem(
|
||||
modifier = Modifier.clickable { onValueChange(!value) },
|
||||
modifier = Modifier
|
||||
.clickable { onValueChange(!value) }
|
||||
.then(modifier),
|
||||
headlineContent = stringResource(headline),
|
||||
supportingContent = stringResource(description),
|
||||
trailingContent = {
|
||||
|
|
|
@ -3,22 +3,24 @@ package app.revanced.manager.ui.screen.settings.update
|
|||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.filled.Update
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.revanced.manager.R
|
||||
import app.revanced.manager.domain.manager.PreferencesManager
|
||||
import app.revanced.manager.ui.component.AppTopBar
|
||||
import app.revanced.manager.ui.component.NotificationCard
|
||||
import app.revanced.manager.ui.component.settings.BooleanItem
|
||||
import app.revanced.manager.ui.component.settings.SettingsListItem
|
||||
import app.revanced.manager.ui.viewmodel.UpdatesSettingsViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
import org.koin.compose.rememberKoinInject
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
@ -26,25 +28,9 @@ fun UpdatesSettingsScreen(
|
|||
onBackClick: () -> Unit,
|
||||
onChangelogClick: () -> Unit,
|
||||
onUpdateClick: () -> Unit,
|
||||
vm: UpdatesSettingsViewModel = getViewModel(),
|
||||
) {
|
||||
val listItems = listOf(
|
||||
Triple(
|
||||
stringResource(R.string.update_channel),
|
||||
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 = onChangelogClick
|
||||
),
|
||||
)
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
|
@ -60,22 +46,29 @@ fun UpdatesSettingsScreen(
|
|||
.padding(paddingValues)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
NotificationCard(
|
||||
text = stringResource(R.string.update_notification),
|
||||
icon = Icons.Default.Update,
|
||||
primaryAction = onUpdateClick
|
||||
SettingsListItem(
|
||||
modifier = Modifier.clickable {
|
||||
coroutineScope.launch {
|
||||
if (vm.checkForUpdates()) onUpdateClick()
|
||||
}
|
||||
},
|
||||
headlineContent = stringResource(R.string.manual_update_check),
|
||||
supportingContent = stringResource(R.string.manual_update_check_description)
|
||||
)
|
||||
|
||||
listItems.forEach { (title, description, onClick) ->
|
||||
SettingsListItem(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp)
|
||||
.clickable { onClick() },
|
||||
headlineContent = title,
|
||||
supportingContent = description
|
||||
SettingsListItem(
|
||||
modifier = Modifier.clickable(onClick = onChangelogClick),
|
||||
headlineContent = stringResource(R.string.changelog),
|
||||
supportingContent = stringResource(
|
||||
R.string.changelog_description
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
BooleanItem(
|
||||
preference = vm.managerAutoUpdates,
|
||||
headline = R.string.update_checking_manager,
|
||||
description = R.string.update_checking_manager_description
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ class ChangelogsViewModel(
|
|||
uiSafe(app, R.string.changelog_download_fail, "Failed to download changelog") {
|
||||
changelogs = api.getReleases("revanced-manager").getOrNull().orEmpty().map { release ->
|
||||
Changelog(
|
||||
release.metadata.tag,
|
||||
release.version,
|
||||
release.findAssetByType(APK_MIMETYPE).downloadCount,
|
||||
release.metadata.publishedAt,
|
||||
release.metadata.body
|
||||
|
|
|
@ -27,8 +27,11 @@ import app.revanced.manager.network.utils.getOrThrow
|
|||
import app.revanced.manager.ui.theme.Theme
|
||||
import app.revanced.manager.util.tag
|
||||
import app.revanced.manager.util.toast
|
||||
import app.revanced.manager.util.uiSafe
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
|
@ -55,13 +58,8 @@ class MainViewModel(
|
|||
private suspend fun checkForManagerUpdates() {
|
||||
if (!prefs.managerAutoUpdates.get() || !networkInfo.isConnected()) return
|
||||
|
||||
try {
|
||||
reVancedAPI.getLatestRelease("revanced-manager").getOrThrow().let { release ->
|
||||
updatedManagerVersion = release.metadata.tag.takeIf { it != Build.VERSION.RELEASE }
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
app.toast(app.getString(R.string.failed_to_check_updates))
|
||||
Log.e(tag, "Failed to check for updates", e)
|
||||
uiSafe(app, R.string.failed_to_check_updates, "Failed to check for updates") {
|
||||
updatedManagerVersion = reVancedAPI.getAppUpdate()?.version
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.revanced.manager.R
|
||||
import app.revanced.manager.data.platform.Filesystem
|
||||
import app.revanced.manager.data.platform.NetworkInfo
|
||||
import app.revanced.manager.network.api.ReVancedAPI
|
||||
import app.revanced.manager.network.api.ReVancedAPI.Extensions.findAssetByType
|
||||
|
@ -47,6 +48,7 @@ class UpdateViewModel(
|
|||
private val http: HttpService by inject()
|
||||
private val pm: PM by inject()
|
||||
private val networkInfo: NetworkInfo by inject()
|
||||
private val fs: Filesystem by inject()
|
||||
|
||||
var downloadedSize by mutableStateOf(0L)
|
||||
private set
|
||||
|
@ -65,17 +67,16 @@ class UpdateViewModel(
|
|||
|
||||
var changelog: Changelog? by mutableStateOf(null)
|
||||
|
||||
private val location = File.createTempFile("updater", ".apk", app.cacheDir)
|
||||
private val location = fs.tempDir.resolve("updater.apk")
|
||||
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()
|
||||
val response = reVancedAPI.getAppUpdate() ?: throw Exception("No update available")
|
||||
|
||||
release = response
|
||||
changelog = Changelog(
|
||||
response.metadata.tag,
|
||||
response.version,
|
||||
response.findAssetByType(APK_MIMETYPE).downloadCount,
|
||||
response.metadata.publishedAt,
|
||||
response.metadata.body
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package app.revanced.manager.ui.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.ViewModel
|
||||
import app.revanced.manager.R
|
||||
import app.revanced.manager.domain.manager.PreferencesManager
|
||||
import app.revanced.manager.network.api.ReVancedAPI
|
||||
import app.revanced.manager.util.toast
|
||||
import app.revanced.manager.util.uiSafe
|
||||
|
||||
class UpdatesSettingsViewModel(
|
||||
prefs: PreferencesManager,
|
||||
private val app: Application,
|
||||
private val reVancedAPI: ReVancedAPI,
|
||||
) : ViewModel() {
|
||||
val managerAutoUpdates = prefs.managerAutoUpdates
|
||||
|
||||
suspend fun checkForUpdates(): Boolean {
|
||||
uiSafe(app, R.string.failed_to_check_updates, "Failed to check for updates") {
|
||||
app.toast(app.getString(R.string.update_check))
|
||||
|
||||
if (reVancedAPI.getAppUpdate() == null)
|
||||
app.toast(app.getString(R.string.no_update_available))
|
||||
else
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -281,11 +281,10 @@
|
|||
<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_channel">Update channel</string>
|
||||
<string name="update_channel_description">Stable</string>
|
||||
<string name="update_notifications">Update notifications</string>
|
||||
<string name="update_notifications_description">Dialog on app launch + badges</string>
|
||||
<string name="manual_update_check">Check for updates</string>
|
||||
<string name="manual_update_check_description">Manually check for updates</string>
|
||||
<string name="update_checking_manager">Update checking</string>
|
||||
<string name="update_checking_manager_description">Check for new versions of ReVanced Manager when the application starts</string>
|
||||
<string name="changelog">Changelog</string>
|
||||
<string name="changelog_loading">Loading changelog</string>
|
||||
<string name="changelog_download_fail">Failed to download changelog: %s</string>
|
||||
|
@ -306,7 +305,9 @@
|
|||
<string name="invalid_date">Invalid date</string>
|
||||
<string name="disable_battery_optimization">Disable battery optimization</string>
|
||||
|
||||
<string name="failed_to_check_updates">Failed to check for updates</string>
|
||||
<string name="failed_to_check_updates">Failed to check for updates: %s</string>
|
||||
<string name="no_update_available">No update available</string>
|
||||
<string name="update_check">Checking for updates…</string>
|
||||
<string name="dismiss_temporary">Not now</string>
|
||||
<string name="update_available_dialog_title">New update available</string>
|
||||
<string name="update_available_dialog_description">A new version (%s) is available for download.</string>
|
||||
|
|
Loading…
Reference in a new issue