mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2024-11-10 01:01:56 +01:00
feat: move update to notification card (#1917)
This commit is contained in:
parent
4e7d96e91d
commit
6bfd9098d6
11 changed files with 222 additions and 265 deletions
|
@ -5,16 +5,8 @@ import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.outlined.Update
|
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import app.revanced.manager.ui.component.AutoUpdatesDialog
|
|
||||||
import app.revanced.manager.ui.destination.Destination
|
import app.revanced.manager.ui.destination.Destination
|
||||||
import app.revanced.manager.ui.destination.SettingsDestination
|
import app.revanced.manager.ui.destination.SettingsDestination
|
||||||
import app.revanced.manager.ui.screen.AppSelectorScreen
|
import app.revanced.manager.ui.screen.AppSelectorScreen
|
||||||
|
@ -46,7 +38,6 @@ class MainActivity : ComponentActivity() {
|
||||||
installSplashScreen()
|
installSplashScreen()
|
||||||
|
|
||||||
val vm: MainViewModel = getAndroidViewModel()
|
val vm: MainViewModel = getAndroidViewModel()
|
||||||
|
|
||||||
vm.importLegacySettings(this)
|
vm.importLegacySettings(this)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
|
@ -59,37 +50,8 @@ class MainActivity : ComponentActivity() {
|
||||||
) {
|
) {
|
||||||
val navController =
|
val navController =
|
||||||
rememberNavController<Destination>(startDestination = Destination.Dashboard)
|
rememberNavController<Destination>(startDestination = Destination.Dashboard)
|
||||||
|
|
||||||
NavBackHandler(navController)
|
NavBackHandler(navController)
|
||||||
|
|
||||||
val firstLaunch by vm.prefs.firstLaunch.getAsState()
|
|
||||||
|
|
||||||
if (firstLaunch) AutoUpdatesDialog(vm::applyAutoUpdatePrefs)
|
|
||||||
|
|
||||||
vm.updatedManagerVersion?.let {
|
|
||||||
AlertDialog(
|
|
||||||
onDismissRequest = vm::dismissUpdateDialog,
|
|
||||||
confirmButton = {
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
vm.dismissUpdateDialog()
|
|
||||||
navController.navigate(Destination.Settings(SettingsDestination.Update(false)))
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.update))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dismissButton = {
|
|
||||||
TextButton(onClick = vm::dismissUpdateDialog) {
|
|
||||||
Text(stringResource(R.string.dismiss_temporary))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon = { Icon(Icons.Outlined.Update, null) },
|
|
||||||
title = { Text(stringResource(R.string.update_available_dialog_title)) },
|
|
||||||
text = { Text(stringResource(R.string.update_available_dialog_description, it)) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimatedNavHost(
|
AnimatedNavHost(
|
||||||
controller = navController
|
controller = navController
|
||||||
) { destination ->
|
) { destination ->
|
||||||
|
@ -97,6 +59,9 @@ class MainActivity : ComponentActivity() {
|
||||||
is Destination.Dashboard -> DashboardScreen(
|
is Destination.Dashboard -> DashboardScreen(
|
||||||
onSettingsClick = { navController.navigate(Destination.Settings()) },
|
onSettingsClick = { navController.navigate(Destination.Settings()) },
|
||||||
onAppSelectorClick = { navController.navigate(Destination.AppSelector) },
|
onAppSelectorClick = { navController.navigate(Destination.AppSelector) },
|
||||||
|
onUpdateClick = { navController.navigate(
|
||||||
|
Destination.Settings(SettingsDestination.Update())
|
||||||
|
) },
|
||||||
onAppClick = { installedApp ->
|
onAppClick = { installedApp ->
|
||||||
navController.navigate(
|
navController.navigate(
|
||||||
Destination.InstalledApplicationInfo(
|
Destination.InstalledApplicationInfo(
|
||||||
|
|
|
@ -4,7 +4,6 @@ import androidx.annotation.StringRes
|
||||||
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.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Source
|
import androidx.compose.material.icons.outlined.Source
|
||||||
import androidx.compose.material.icons.outlined.Update
|
import androidx.compose.material.icons.outlined.Update
|
||||||
|
@ -13,7 +12,6 @@ import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
@ -21,11 +19,9 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
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.graphics.vector.ImageVector
|
||||||
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.R
|
import app.revanced.manager.R
|
||||||
|
|
||||||
|
@ -37,52 +33,35 @@ fun AutoUpdatesDialog(onSubmit: (Boolean, Boolean) -> Unit) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = {},
|
onDismissRequest = {},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(onClick = { onSubmit(managerEnabled, patchesEnabled) }) {
|
||||||
onClick = { onSubmit(managerEnabled, patchesEnabled) }
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.save))
|
Text(stringResource(R.string.save))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon = {
|
icon = { Icon(Icons.Outlined.Update, null) },
|
||||||
Icon(Icons.Outlined.Update, null)
|
title = { Text(text = stringResource(R.string.auto_updates_dialog_title)) },
|
||||||
},
|
|
||||||
title = {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.auto_updates_dialog_title),
|
|
||||||
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center),
|
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
text = {
|
text = {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(text = stringResource(R.string.auto_updates_dialog_description))
|
||||||
text = stringResource(R.string.auto_updates_dialog_description),
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
)
|
|
||||||
|
|
||||||
AutoUpdatesItem(
|
Column {
|
||||||
headline = R.string.auto_updates_dialog_manager,
|
AutoUpdatesItem(
|
||||||
icon = Icons.Outlined.Update,
|
headline = R.string.auto_updates_dialog_manager,
|
||||||
checked = managerEnabled,
|
icon = Icons.Outlined.Update,
|
||||||
onCheckedChange = { managerEnabled = it }
|
checked = managerEnabled,
|
||||||
)
|
onCheckedChange = { managerEnabled = it }
|
||||||
HorizontalDivider()
|
)
|
||||||
AutoUpdatesItem(
|
HorizontalDivider()
|
||||||
headline = R.string.auto_updates_dialog_patches,
|
AutoUpdatesItem(
|
||||||
icon = Icons.Outlined.Source,
|
headline = R.string.auto_updates_dialog_patches,
|
||||||
checked = patchesEnabled,
|
icon = Icons.Outlined.Source,
|
||||||
onCheckedChange = { patchesEnabled = it }
|
checked = patchesEnabled,
|
||||||
)
|
onCheckedChange = { patchesEnabled = it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Text(
|
Text(text = stringResource(R.string.auto_updates_dialog_note))
|
||||||
text = stringResource(R.string.auto_updates_dialog_note),
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -94,22 +73,9 @@ private fun AutoUpdatesItem(
|
||||||
icon: ImageVector,
|
icon: ImageVector,
|
||||||
checked: Boolean,
|
checked: Boolean,
|
||||||
onCheckedChange: (Boolean) -> Unit
|
onCheckedChange: (Boolean) -> Unit
|
||||||
) {
|
) = ListItem(
|
||||||
ListItem(
|
leadingContent = { Icon(icon, null) },
|
||||||
leadingContent = { Icon(icon, null, tint = MaterialTheme.colorScheme.onSurface) },
|
headlineContent = { Text(stringResource(headline)) },
|
||||||
headlineContent = {
|
trailingContent = { Checkbox(checked = checked, onCheckedChange = null) },
|
||||||
Text(
|
modifier = Modifier.clickable { onCheckedChange(!checked) }
|
||||||
text = stringResource(headline),
|
)
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
|
||||||
)
|
|
||||||
},
|
|
||||||
trailingContent = {
|
|
||||||
Checkbox(
|
|
||||||
checked = checked,
|
|
||||||
onCheckedChange = onCheckedChange
|
|
||||||
)
|
|
||||||
},
|
|
||||||
modifier = Modifier.clickable { onCheckedChange(!checked) }
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,8 +1,10 @@
|
||||||
package app.revanced.manager.ui.component
|
package app.revanced.manager.ui.component
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
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.RowScope
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
@ -11,7 +13,6 @@ import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Close
|
import androidx.compose.material.icons.outlined.Close
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
@ -27,74 +28,73 @@ import app.revanced.manager.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NotificationCard(
|
fun NotificationCard(
|
||||||
isWarning: Boolean = false,
|
|
||||||
title: String? = null,
|
|
||||||
text: String,
|
text: String,
|
||||||
icon: ImageVector,
|
icon: ImageVector,
|
||||||
actions: (@Composable () -> Unit)?
|
modifier: Modifier = Modifier,
|
||||||
|
actions: (@Composable RowScope.() -> Unit)? = null,
|
||||||
|
title: String? = null,
|
||||||
|
isWarning: Boolean = false
|
||||||
) {
|
) {
|
||||||
val color =
|
val color =
|
||||||
if (isWarning) MaterialTheme.colorScheme.onError else MaterialTheme.colorScheme.onPrimaryContainer
|
if (isWarning) MaterialTheme.colorScheme.onError else MaterialTheme.colorScheme.onPrimaryContainer
|
||||||
|
|
||||||
NotificationCardInstance(isWarning = isWarning) {
|
NotificationCardInstance(modifier = modifier, isWarning = isWarning) {
|
||||||
Column(
|
Row(
|
||||||
modifier = Modifier.padding(if (title != null) 20.dp else 16.dp),
|
modifier = Modifier.padding(16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
if (title != null) {
|
Box(
|
||||||
|
modifier = Modifier.size(28.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
modifier = Modifier.size(36.dp),
|
modifier = Modifier.size(24.dp),
|
||||||
imageVector = icon,
|
imageVector = icon,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = color,
|
tint = color,
|
||||||
)
|
)
|
||||||
Column(
|
}
|
||||||
verticalArrangement = Arrangement.spacedBy(6.dp)
|
|
||||||
) {
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
title?.let {
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = it,
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
color = color,
|
color = color,
|
||||||
)
|
)
|
||||||
Text(
|
|
||||||
text = text,
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = color,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else {
|
Text(
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
|
text = text,
|
||||||
Icon(
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
modifier = Modifier.size(24.dp),
|
color = color,
|
||||||
imageVector = icon,
|
)
|
||||||
contentDescription = null,
|
Row(
|
||||||
tint = color,
|
horizontalArrangement = Arrangement.End,
|
||||||
)
|
modifier = Modifier.fillMaxWidth()
|
||||||
Text(
|
) {
|
||||||
text = text,
|
actions?.invoke(this)
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = color,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actions?.invoke()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NotificationCard(
|
fun NotificationCard(
|
||||||
isWarning: Boolean = false,
|
|
||||||
title: String? = null,
|
|
||||||
text: String,
|
text: String,
|
||||||
icon: ImageVector,
|
icon: ImageVector,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
title: String? = null,
|
||||||
|
isWarning: Boolean = false,
|
||||||
onDismiss: (() -> Unit)? = null,
|
onDismiss: (() -> Unit)? = null,
|
||||||
primaryAction: (() -> Unit)? = null
|
onClick: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
val color =
|
val color =
|
||||||
if (isWarning) MaterialTheme.colorScheme.onError else MaterialTheme.colorScheme.onPrimaryContainer
|
if (isWarning) MaterialTheme.colorScheme.onError else MaterialTheme.colorScheme.onPrimaryContainer
|
||||||
|
|
||||||
NotificationCardInstance(isWarning = isWarning, onClick = primaryAction) {
|
NotificationCardInstance(modifier = modifier, isWarning = isWarning, onClick = onClick) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
@ -102,12 +102,17 @@ fun NotificationCard(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
) {
|
) {
|
||||||
Icon(
|
Box(
|
||||||
modifier = Modifier.size(if (title != null) 36.dp else 24.dp),
|
modifier = Modifier.size(28.dp),
|
||||||
imageVector = icon,
|
contentAlignment = Alignment.Center
|
||||||
contentDescription = null,
|
) {
|
||||||
tint = color,
|
Icon(
|
||||||
)
|
modifier = Modifier.size(24.dp),
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = color,
|
||||||
|
)
|
||||||
|
}
|
||||||
if (title != null) {
|
if (title != null) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
|
@ -145,32 +150,31 @@ fun NotificationCard(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun NotificationCardInstance(
|
private fun NotificationCardInstance(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
isWarning: Boolean = false,
|
isWarning: Boolean = false,
|
||||||
onClick: (() -> Unit)? = null,
|
onClick: (() -> Unit)? = null,
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
val colors =
|
val colors =
|
||||||
CardDefaults.cardColors(containerColor = if (isWarning) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primaryContainer)
|
CardDefaults.cardColors(containerColor = if (isWarning) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primaryContainer)
|
||||||
val modifier = Modifier
|
val defaultModifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp)
|
|
||||||
.clip(RoundedCornerShape(24.dp))
|
.clip(RoundedCornerShape(24.dp))
|
||||||
|
|
||||||
if (onClick != null) {
|
if (onClick != null) {
|
||||||
Card(
|
Card(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
colors = colors,
|
colors = colors,
|
||||||
modifier = modifier
|
modifier = modifier.then(defaultModifier)
|
||||||
) {
|
) {
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Card(
|
Card(
|
||||||
colors = colors,
|
colors = colors,
|
||||||
modifier = modifier,
|
modifier = modifier.then(defaultModifier)
|
||||||
) {
|
) {
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,34 +65,22 @@ fun BundleItem(
|
||||||
.height(64.dp)
|
.height(64.dp)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = {
|
onClick = { viewBundleDialogPage = true },
|
||||||
viewBundleDialogPage = true
|
|
||||||
},
|
|
||||||
onLongClick = onSelect,
|
onLongClick = onSelect,
|
||||||
),
|
),
|
||||||
leadingContent = {
|
leadingContent = if (selectable) {
|
||||||
if(selectable) {
|
{
|
||||||
Checkbox(
|
Checkbox(
|
||||||
checked = isBundleSelected,
|
checked = isBundleSelected,
|
||||||
onCheckedChange = toggleSelection,
|
onCheckedChange = toggleSelection,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
} else null,
|
||||||
|
|
||||||
headlineContent = {
|
headlineContent = { Text(text = bundle.name) },
|
||||||
Text(
|
|
||||||
text = bundle.name,
|
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
|
||||||
)
|
|
||||||
},
|
|
||||||
supportingContent = {
|
supportingContent = {
|
||||||
state.patchBundleOrNull()?.patches?.size?.let { patchCount ->
|
state.patchBundleOrNull()?.patches?.size?.let { patchCount ->
|
||||||
Text(
|
Text(text = pluralStringResource(R.plurals.patch_count, patchCount, patchCount))
|
||||||
text = pluralStringResource(R.plurals.patch_count, patchCount, patchCount),
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
|
@ -114,13 +102,7 @@ fun BundleItem(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
version?.let { txt ->
|
version?.let { Text(text = it) }
|
||||||
Text(
|
|
||||||
text = txt,
|
|
||||||
style = MaterialTheme.typography.labelSmall,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,7 +27,7 @@ sealed interface SettingsDestination : Parcelable {
|
||||||
object About : SettingsDestination
|
object About : SettingsDestination
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Update(val downloadOnScreenEntry: Boolean) : SettingsDestination
|
data class Update(val downloadOnScreenEntry: Boolean = false) : SettingsDestination
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object Changelogs : SettingsDestination
|
object Changelogs : SettingsDestination
|
||||||
|
|
|
@ -2,6 +2,7 @@ package app.revanced.manager.ui.screen
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
@ -15,6 +16,8 @@ import androidx.compose.material.icons.outlined.DeleteOutline
|
||||||
import androidx.compose.material.icons.outlined.Refresh
|
import androidx.compose.material.icons.outlined.Refresh
|
||||||
import androidx.compose.material.icons.outlined.Settings
|
import androidx.compose.material.icons.outlined.Settings
|
||||||
import androidx.compose.material.icons.outlined.Source
|
import androidx.compose.material.icons.outlined.Source
|
||||||
|
import androidx.compose.material.icons.outlined.Update
|
||||||
|
import androidx.compose.material.icons.outlined.WarningAmber
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
@ -24,6 +27,7 @@ import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Tab
|
import androidx.compose.material3.Tab
|
||||||
import androidx.compose.material3.TabRow
|
import androidx.compose.material3.TabRow
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.surfaceColorAtElevation
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
@ -43,7 +47,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.data.room.apps.installed.InstalledApp
|
import app.revanced.manager.data.room.apps.installed.InstalledApp
|
||||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Companion.isDefault
|
import app.revanced.manager.domain.bundles.PatchBundleSource.Companion.isDefault
|
||||||
|
import app.revanced.manager.patcher.aapt.Aapt
|
||||||
import app.revanced.manager.ui.component.AppTopBar
|
import app.revanced.manager.ui.component.AppTopBar
|
||||||
|
import app.revanced.manager.ui.component.AutoUpdatesDialog
|
||||||
|
import app.revanced.manager.ui.component.NotificationCard
|
||||||
import app.revanced.manager.ui.component.bundle.BundleItem
|
import app.revanced.manager.ui.component.bundle.BundleItem
|
||||||
import app.revanced.manager.ui.component.bundle.BundleTopBar
|
import app.revanced.manager.ui.component.bundle.BundleTopBar
|
||||||
import app.revanced.manager.ui.component.bundle.ImportBundleDialog
|
import app.revanced.manager.ui.component.bundle.ImportBundleDialog
|
||||||
|
@ -68,38 +75,26 @@ fun DashboardScreen(
|
||||||
vm: DashboardViewModel = koinViewModel(),
|
vm: DashboardViewModel = koinViewModel(),
|
||||||
onAppSelectorClick: () -> Unit,
|
onAppSelectorClick: () -> Unit,
|
||||||
onSettingsClick: () -> Unit,
|
onSettingsClick: () -> Unit,
|
||||||
|
onUpdateClick: () -> Unit,
|
||||||
onAppClick: (InstalledApp) -> Unit
|
onAppClick: (InstalledApp) -> Unit
|
||||||
) {
|
) {
|
||||||
var showBundleTypeSelectorDialog by rememberSaveable { mutableStateOf(false) }
|
|
||||||
var selectedBundleType: BundleType? by rememberSaveable { mutableStateOf(null) }
|
|
||||||
|
|
||||||
val bundlesSelectable by remember { derivedStateOf { vm.selectedSources.size > 0 } }
|
val bundlesSelectable by remember { derivedStateOf { vm.selectedSources.size > 0 } }
|
||||||
val pages: Array<DashboardPage> = DashboardPage.values()
|
|
||||||
val availablePatches by vm.availablePatches.collectAsStateWithLifecycle(0)
|
val availablePatches by vm.availablePatches.collectAsStateWithLifecycle(0)
|
||||||
val androidContext = LocalContext.current
|
val androidContext = LocalContext.current
|
||||||
|
val composableScope = rememberCoroutineScope()
|
||||||
val pagerState = rememberPagerState(
|
val pagerState = rememberPagerState(
|
||||||
initialPage = DashboardPage.DASHBOARD.ordinal,
|
initialPage = DashboardPage.DASHBOARD.ordinal,
|
||||||
initialPageOffsetFraction = 0f
|
initialPageOffsetFraction = 0f
|
||||||
) {
|
) { DashboardPage.entries.size }
|
||||||
DashboardPage.values().size
|
|
||||||
}
|
|
||||||
val composableScope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
LaunchedEffect(pagerState.currentPage) {
|
LaunchedEffect(pagerState.currentPage) {
|
||||||
if (pagerState.currentPage != DashboardPage.BUNDLES.ordinal) vm.cancelSourceSelection()
|
if (pagerState.currentPage != DashboardPage.BUNDLES.ordinal) vm.cancelSourceSelection()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showBundleTypeSelectorDialog) {
|
val firstLaunch by vm.prefs.firstLaunch.getAsState()
|
||||||
ImportBundleTypeSelectorDialog(
|
if (firstLaunch) AutoUpdatesDialog(vm::applyAutoUpdatePrefs)
|
||||||
onDismiss = { showBundleTypeSelectorDialog = false },
|
|
||||||
onConfirm = {
|
|
||||||
selectedBundleType = it
|
|
||||||
showBundleTypeSelectorDialog = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var selectedBundleType: BundleType? by rememberSaveable { mutableStateOf(null) }
|
||||||
selectedBundleType?.let {
|
selectedBundleType?.let {
|
||||||
fun dismiss() {
|
fun dismiss() {
|
||||||
selectedBundleType = null
|
selectedBundleType = null
|
||||||
|
@ -119,6 +114,17 @@ fun DashboardScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var showBundleTypeSelectorDialog by rememberSaveable { mutableStateOf(false) }
|
||||||
|
if (showBundleTypeSelectorDialog) {
|
||||||
|
ImportBundleTypeSelectorDialog(
|
||||||
|
onDismiss = { showBundleTypeSelectorDialog = false },
|
||||||
|
onConfirm = {
|
||||||
|
selectedBundleType = it
|
||||||
|
showBundleTypeSelectorDialog = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
if (bundlesSelectable) {
|
if (bundlesSelectable) {
|
||||||
|
@ -192,9 +198,7 @@ fun DashboardScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) { Icon(Icons.Default.Add, stringResource(R.string.add)) }
|
||||||
Icon(Icons.Default.Add, stringResource(R.string.add))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Column(Modifier.padding(paddingValues)) {
|
Column(Modifier.padding(paddingValues)) {
|
||||||
|
@ -202,7 +206,7 @@ fun DashboardScreen(
|
||||||
selectedTabIndex = pagerState.currentPage,
|
selectedTabIndex = pagerState.currentPage,
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp)
|
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp)
|
||||||
) {
|
) {
|
||||||
pages.forEachIndexed { index, page ->
|
DashboardPage.entries.forEachIndexed { index, page ->
|
||||||
Tab(
|
Tab(
|
||||||
selected = pagerState.currentPage == index,
|
selected = pagerState.currentPage == index,
|
||||||
onClick = { composableScope.launch { pagerState.animateScrollToPage(index) } },
|
onClick = { composableScope.launch { pagerState.animateScrollToPage(index) } },
|
||||||
|
@ -214,12 +218,41 @@ fun DashboardScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Notifications(
|
||||||
|
if (!Aapt.supportsDevice()) {
|
||||||
|
{
|
||||||
|
NotificationCard(
|
||||||
|
isWarning = true,
|
||||||
|
icon = Icons.Outlined.WarningAmber,
|
||||||
|
text = stringResource(R.string.unsupported_architecture_warning),
|
||||||
|
onDismiss = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else null,
|
||||||
|
vm.updatedManagerVersion?.let {
|
||||||
|
{
|
||||||
|
NotificationCard(
|
||||||
|
text = stringResource(R.string.update_available_dialog_description, it),
|
||||||
|
icon = Icons.Outlined.Update,
|
||||||
|
actions = {
|
||||||
|
TextButton(onClick = vm::dismissUpdateDialog) {
|
||||||
|
Text(stringResource(R.string.dismiss))
|
||||||
|
}
|
||||||
|
TextButton(onClick = onUpdateClick) {
|
||||||
|
Text(stringResource(R.string.update))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
state = pagerState,
|
state = pagerState,
|
||||||
userScrollEnabled = true,
|
userScrollEnabled = true,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
pageContent = { index ->
|
pageContent = { index ->
|
||||||
when (pages[index]) {
|
when (DashboardPage.entries[index]) {
|
||||||
DashboardPage.DASHBOARD -> {
|
DashboardPage.DASHBOARD -> {
|
||||||
InstalledAppsScreen(
|
InstalledAppsScreen(
|
||||||
onAppClick = onAppClick
|
onAppClick = onAppClick
|
||||||
|
@ -238,11 +271,9 @@ fun DashboardScreen(
|
||||||
val sources by vm.sources.collectAsStateWithLifecycle(initialValue = emptyList())
|
val sources by vm.sources.collectAsStateWithLifecycle(initialValue = emptyList())
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxSize(),
|
||||||
.fillMaxSize(),
|
|
||||||
) {
|
) {
|
||||||
sources.forEach {
|
sources.forEach {
|
||||||
|
|
||||||
BundleItem(
|
BundleItem(
|
||||||
bundle = it,
|
bundle = it,
|
||||||
onDelete = {
|
onDelete = {
|
||||||
|
@ -272,4 +303,22 @@ fun DashboardScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Notifications(
|
||||||
|
vararg notifications: (@Composable () -> Unit)?,
|
||||||
|
) {
|
||||||
|
val activeNotifications = notifications.filterNotNull()
|
||||||
|
|
||||||
|
if (activeNotifications.isNotEmpty()) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
activeNotifications.forEach { notification ->
|
||||||
|
notification()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -6,8 +6,6 @@ import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.outlined.WarningAmber
|
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
@ -20,12 +18,10 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.data.room.apps.installed.InstalledApp
|
import app.revanced.manager.data.room.apps.installed.InstalledApp
|
||||||
import app.revanced.manager.patcher.aapt.Aapt
|
|
||||||
import app.revanced.manager.ui.component.AppIcon
|
import app.revanced.manager.ui.component.AppIcon
|
||||||
import app.revanced.manager.ui.component.AppLabel
|
import app.revanced.manager.ui.component.AppLabel
|
||||||
import app.revanced.manager.ui.component.LazyColumnWithScrollbar
|
import app.revanced.manager.ui.component.LazyColumnWithScrollbar
|
||||||
import app.revanced.manager.ui.component.LoadingIndicator
|
import app.revanced.manager.ui.component.LoadingIndicator
|
||||||
import app.revanced.manager.ui.component.NotificationCard
|
|
||||||
import app.revanced.manager.ui.viewmodel.InstalledAppsViewModel
|
import app.revanced.manager.ui.viewmodel.InstalledAppsViewModel
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
|
@ -37,16 +33,6 @@ fun InstalledAppsScreen(
|
||||||
val installedApps by viewModel.apps.collectAsStateWithLifecycle(initialValue = null)
|
val installedApps by viewModel.apps.collectAsStateWithLifecycle(initialValue = null)
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
if (!Aapt.supportsDevice()) {
|
|
||||||
NotificationCard(
|
|
||||||
isWarning = true,
|
|
||||||
icon = Icons.Outlined.WarningAmber,
|
|
||||||
text = stringResource(
|
|
||||||
R.string.unsupported_architecture_warning
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
LazyColumnWithScrollbar(
|
LazyColumnWithScrollbar(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
|
|
@ -22,6 +22,7 @@ import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
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.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.ColumnWithScrollbar
|
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
||||||
|
@ -161,10 +162,11 @@ fun SettingsScreen(
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(visible = showBatteryButton) {
|
AnimatedVisibility(visible = showBatteryButton) {
|
||||||
NotificationCard(
|
NotificationCard(
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
isWarning = true,
|
isWarning = true,
|
||||||
icon = Icons.Default.BatteryAlert,
|
icon = Icons.Default.BatteryAlert,
|
||||||
text = stringResource(R.string.battery_optimization_notification),
|
text = stringResource(R.string.battery_optimization_notification),
|
||||||
primaryAction = {
|
onClick = {
|
||||||
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}")
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,21 +3,32 @@ package app.revanced.manager.ui.viewmodel
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
|
import app.revanced.manager.data.platform.NetworkInfo
|
||||||
import app.revanced.manager.domain.bundles.PatchBundleSource
|
import app.revanced.manager.domain.bundles.PatchBundleSource
|
||||||
|
import app.revanced.manager.domain.bundles.PatchBundleSource.Companion.asRemoteOrNull
|
||||||
import app.revanced.manager.domain.bundles.RemotePatchBundle
|
import app.revanced.manager.domain.bundles.RemotePatchBundle
|
||||||
|
import app.revanced.manager.domain.manager.PreferencesManager
|
||||||
import app.revanced.manager.domain.repository.PatchBundleRepository
|
import app.revanced.manager.domain.repository.PatchBundleRepository
|
||||||
|
import app.revanced.manager.network.api.ReVancedAPI
|
||||||
import app.revanced.manager.util.toast
|
import app.revanced.manager.util.toast
|
||||||
import app.revanced.manager.util.uiSafe
|
import app.revanced.manager.util.uiSafe
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class DashboardViewModel(
|
class DashboardViewModel(
|
||||||
private val app: Application,
|
private val app: Application,
|
||||||
private val patchBundleRepository: PatchBundleRepository
|
private val patchBundleRepository: PatchBundleRepository,
|
||||||
|
private val reVancedAPI: ReVancedAPI,
|
||||||
|
private val networkInfo: NetworkInfo,
|
||||||
|
val prefs: PreferencesManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val availablePatches =
|
val availablePatches =
|
||||||
patchBundleRepository.bundles.map { it.values.sumOf { bundle -> bundle.patches.size } }
|
patchBundleRepository.bundles.map { it.values.sumOf { bundle -> bundle.patches.size } }
|
||||||
|
@ -25,6 +36,47 @@ class DashboardViewModel(
|
||||||
val sources = patchBundleRepository.sources
|
val sources = patchBundleRepository.sources
|
||||||
val selectedSources = mutableStateListOf<PatchBundleSource>()
|
val selectedSources = mutableStateListOf<PatchBundleSource>()
|
||||||
|
|
||||||
|
|
||||||
|
var updatedManagerVersion: String? by mutableStateOf(null)
|
||||||
|
private set
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch { checkForManagerUpdates() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismissUpdateDialog() {
|
||||||
|
updatedManagerVersion = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun checkForManagerUpdates() {
|
||||||
|
if (!prefs.managerAutoUpdates.get() || !networkInfo.isConnected()) return
|
||||||
|
|
||||||
|
uiSafe(app, R.string.failed_to_check_updates, "Failed to check for updates") {
|
||||||
|
updatedManagerVersion = reVancedAPI.getAppUpdate()?.version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun applyAutoUpdatePrefs(manager: Boolean, patches: Boolean) = viewModelScope.launch {
|
||||||
|
prefs.firstLaunch.update(false)
|
||||||
|
|
||||||
|
prefs.managerAutoUpdates.update(manager)
|
||||||
|
|
||||||
|
if (manager) checkForManagerUpdates()
|
||||||
|
|
||||||
|
if (patches) {
|
||||||
|
with(patchBundleRepository) {
|
||||||
|
sources
|
||||||
|
.first()
|
||||||
|
.find { it.uid == 0 }
|
||||||
|
?.asRemoteOrNull
|
||||||
|
?.setAutoUpdate(true)
|
||||||
|
|
||||||
|
updateCheck()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun cancelSourceSelection() {
|
fun cancelSourceSelection() {
|
||||||
selectedSources.clear()
|
selectedSources.clear()
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,25 +8,18 @@ import android.util.Log
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.data.platform.NetworkInfo
|
|
||||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Companion.asRemoteOrNull
|
import app.revanced.manager.domain.bundles.PatchBundleSource.Companion.asRemoteOrNull
|
||||||
import app.revanced.manager.domain.manager.KeystoreManager
|
import app.revanced.manager.domain.manager.KeystoreManager
|
||||||
import app.revanced.manager.domain.manager.PreferencesManager
|
import app.revanced.manager.domain.manager.PreferencesManager
|
||||||
import app.revanced.manager.domain.repository.PatchBundleRepository
|
import app.revanced.manager.domain.repository.PatchBundleRepository
|
||||||
import app.revanced.manager.domain.repository.PatchSelectionRepository
|
import app.revanced.manager.domain.repository.PatchSelectionRepository
|
||||||
import app.revanced.manager.domain.repository.SerializedSelection
|
import app.revanced.manager.domain.repository.SerializedSelection
|
||||||
import app.revanced.manager.network.api.ReVancedAPI
|
|
||||||
import app.revanced.manager.ui.theme.Theme
|
import app.revanced.manager.ui.theme.Theme
|
||||||
import app.revanced.manager.util.isDebuggable
|
|
||||||
import app.revanced.manager.util.tag
|
import app.revanced.manager.util.tag
|
||||||
import app.revanced.manager.util.toast
|
import app.revanced.manager.util.toast
|
||||||
import app.revanced.manager.util.uiSafe
|
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
@ -36,50 +29,9 @@ class MainViewModel(
|
||||||
private val patchBundleRepository: PatchBundleRepository,
|
private val patchBundleRepository: PatchBundleRepository,
|
||||||
private val patchSelectionRepository: PatchSelectionRepository,
|
private val patchSelectionRepository: PatchSelectionRepository,
|
||||||
private val keystoreManager: KeystoreManager,
|
private val keystoreManager: KeystoreManager,
|
||||||
private val reVancedAPI: ReVancedAPI,
|
|
||||||
private val app: Application,
|
private val app: Application,
|
||||||
private val networkInfo: NetworkInfo,
|
|
||||||
val prefs: PreferencesManager
|
val prefs: PreferencesManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
var updatedManagerVersion: String? by mutableStateOf(null)
|
|
||||||
private set
|
|
||||||
|
|
||||||
init {
|
|
||||||
viewModelScope.launch { checkForManagerUpdates() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun dismissUpdateDialog() {
|
|
||||||
updatedManagerVersion = null
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun checkForManagerUpdates() {
|
|
||||||
if (app.isDebuggable || !prefs.managerAutoUpdates.get() || !networkInfo.isConnected()) return
|
|
||||||
|
|
||||||
uiSafe(app, R.string.failed_to_check_updates, "Failed to check for updates") {
|
|
||||||
updatedManagerVersion = reVancedAPI.getAppUpdate()?.version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun applyAutoUpdatePrefs(manager: Boolean, patches: Boolean) = viewModelScope.launch {
|
|
||||||
prefs.firstLaunch.update(false)
|
|
||||||
|
|
||||||
prefs.managerAutoUpdates.update(manager)
|
|
||||||
|
|
||||||
if (manager) checkForManagerUpdates()
|
|
||||||
|
|
||||||
if (patches) {
|
|
||||||
with(patchBundleRepository) {
|
|
||||||
sources
|
|
||||||
.first()
|
|
||||||
.find { it.uid == 0 }
|
|
||||||
?.asRemoteOrNull
|
|
||||||
?.setAutoUpdate(true)
|
|
||||||
|
|
||||||
updateCheck()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun importLegacySettings(componentActivity: ComponentActivity) {
|
fun importLegacySettings(componentActivity: ComponentActivity) {
|
||||||
if (!prefs.firstLaunch.getBlocking()) return
|
if (!prefs.firstLaunch.getBlocking()) return
|
||||||
|
|
||||||
|
|
|
@ -324,8 +324,7 @@
|
||||||
<string name="no_update_available">No update available</string>
|
<string name="no_update_available">No update available</string>
|
||||||
<string name="update_check">Checking for updates…</string>
|
<string name="update_check">Checking for updates…</string>
|
||||||
<string name="dismiss_temporary">Not now</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 of ReVanced Manager (%s) is available.</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="failed_to_download_update">Failed to download update: %s</string>
|
||||||
<string name="download">Download</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_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>
|
||||||
|
|
Loading…
Reference in a new issue