diff --git a/app/src/main/java/app/revanced/manager/MainActivity.kt b/app/src/main/java/app/revanced/manager/MainActivity.kt index e3202399..4c8d9ef7 100644 --- a/app/src/main/java/app/revanced/manager/MainActivity.kt +++ b/app/src/main/java/app/revanced/manager/MainActivity.kt @@ -5,16 +5,8 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.animation.ExperimentalAnimationApi 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.ui.res.stringResource 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.SettingsDestination import app.revanced.manager.ui.screen.AppSelectorScreen @@ -46,7 +38,6 @@ class MainActivity : ComponentActivity() { installSplashScreen() val vm: MainViewModel = getAndroidViewModel() - vm.importLegacySettings(this) setContent { @@ -59,37 +50,8 @@ class MainActivity : ComponentActivity() { ) { val navController = rememberNavController(startDestination = Destination.Dashboard) - 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( controller = navController ) { destination -> @@ -97,6 +59,9 @@ class MainActivity : ComponentActivity() { is Destination.Dashboard -> DashboardScreen( onSettingsClick = { navController.navigate(Destination.Settings()) }, onAppSelectorClick = { navController.navigate(Destination.AppSelector) }, + onUpdateClick = { navController.navigate( + Destination.Settings(SettingsDestination.Update()) + ) }, onAppClick = { installedApp -> navController.navigate( Destination.InstalledApplicationInfo( diff --git a/app/src/main/java/app/revanced/manager/ui/component/AutoUpdatesDialog.kt b/app/src/main/java/app/revanced/manager/ui/component/AutoUpdatesDialog.kt index 130c5ffe..9da4f27f 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/AutoUpdatesDialog.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/AutoUpdatesDialog.kt @@ -4,7 +4,6 @@ import androidx.annotation.StringRes import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Source 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.Icon import androidx.compose.material3.ListItem -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -21,11 +19,9 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import app.revanced.manager.R @@ -37,52 +33,35 @@ fun AutoUpdatesDialog(onSubmit: (Boolean, Boolean) -> Unit) { AlertDialog( onDismissRequest = {}, confirmButton = { - TextButton( - onClick = { onSubmit(managerEnabled, patchesEnabled) } - ) { + TextButton(onClick = { onSubmit(managerEnabled, patchesEnabled) }) { Text(stringResource(R.string.save)) } }, - icon = { - Icon(Icons.Outlined.Update, null) - }, - title = { - Text( - text = stringResource(R.string.auto_updates_dialog_title), - style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center), - color = MaterialTheme.colorScheme.onSurface, - ) - }, + icon = { Icon(Icons.Outlined.Update, null) }, + title = { Text(text = stringResource(R.string.auto_updates_dialog_title)) }, text = { Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), ) { - Text( - text = stringResource(R.string.auto_updates_dialog_description), - style = MaterialTheme.typography.bodyMedium, - ) + Text(text = stringResource(R.string.auto_updates_dialog_description)) - AutoUpdatesItem( - headline = R.string.auto_updates_dialog_manager, - icon = Icons.Outlined.Update, - checked = managerEnabled, - onCheckedChange = { managerEnabled = it } - ) - HorizontalDivider() - AutoUpdatesItem( - headline = R.string.auto_updates_dialog_patches, - icon = Icons.Outlined.Source, - checked = patchesEnabled, - onCheckedChange = { patchesEnabled = it } - ) + Column { + AutoUpdatesItem( + headline = R.string.auto_updates_dialog_manager, + icon = Icons.Outlined.Update, + checked = managerEnabled, + onCheckedChange = { managerEnabled = it } + ) + HorizontalDivider() + AutoUpdatesItem( + headline = R.string.auto_updates_dialog_patches, + icon = Icons.Outlined.Source, + checked = patchesEnabled, + onCheckedChange = { patchesEnabled = it } + ) + } - Text( - text = stringResource(R.string.auto_updates_dialog_note), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.fillMaxWidth() - ) + Text(text = stringResource(R.string.auto_updates_dialog_note)) } } ) @@ -94,22 +73,9 @@ private fun AutoUpdatesItem( icon: ImageVector, checked: Boolean, onCheckedChange: (Boolean) -> Unit -) { - ListItem( - leadingContent = { Icon(icon, null, tint = MaterialTheme.colorScheme.onSurface) }, - headlineContent = { - Text( - text = stringResource(headline), - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurface - ) - }, - trailingContent = { - Checkbox( - checked = checked, - onCheckedChange = onCheckedChange - ) - }, - modifier = Modifier.clickable { onCheckedChange(!checked) } - ) -} \ No newline at end of file +) = ListItem( + leadingContent = { Icon(icon, null) }, + headlineContent = { Text(stringResource(headline)) }, + trailingContent = { Checkbox(checked = checked, onCheckedChange = null) }, + modifier = Modifier.clickable { onCheckedChange(!checked) } +) \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/component/NotificationCard.kt b/app/src/main/java/app/revanced/manager/ui/component/NotificationCard.kt index 3b17de5d..0357a18f 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/NotificationCard.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/NotificationCard.kt @@ -1,8 +1,10 @@ package app.revanced.manager.ui.component import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding 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.material3.Card import androidx.compose.material3.CardDefaults -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -27,74 +28,73 @@ import app.revanced.manager.R @Composable fun NotificationCard( - isWarning: Boolean = false, - title: String? = null, text: String, icon: ImageVector, - actions: (@Composable () -> Unit)? + modifier: Modifier = Modifier, + actions: (@Composable RowScope.() -> Unit)? = null, + title: String? = null, + isWarning: Boolean = false ) { val color = if (isWarning) MaterialTheme.colorScheme.onError else MaterialTheme.colorScheme.onPrimaryContainer - NotificationCardInstance(isWarning = isWarning) { - Column( - modifier = Modifier.padding(if (title != null) 20.dp else 16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) + NotificationCardInstance(modifier = modifier, isWarning = isWarning) { + Row( + modifier = Modifier.padding(16.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp) ) { - if (title != null) { + Box( + modifier = Modifier.size(28.dp), + contentAlignment = Alignment.Center + ) { Icon( - modifier = Modifier.size(36.dp), + modifier = Modifier.size(24.dp), imageVector = icon, contentDescription = null, tint = color, ) - Column( - verticalArrangement = Arrangement.spacedBy(6.dp) - ) { + } + + Column( + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + title?.let { Text( - text = title, + text = it, style = MaterialTheme.typography.titleLarge, color = color, ) - Text( - text = text, - style = MaterialTheme.typography.bodyMedium, - color = color, - ) } - } else { - Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) { - Icon( - modifier = Modifier.size(24.dp), - imageVector = icon, - contentDescription = null, - tint = color, - ) - Text( - text = text, - style = MaterialTheme.typography.bodyMedium, - color = color, - ) + Text( + text = text, + style = MaterialTheme.typography.bodyMedium, + color = color, + ) + Row( + horizontalArrangement = Arrangement.End, + modifier = Modifier.fillMaxWidth() + ) { + actions?.invoke(this) } } - actions?.invoke() } } } @Composable fun NotificationCard( - isWarning: Boolean = false, - title: String? = null, text: String, icon: ImageVector, + modifier: Modifier = Modifier, + title: String? = null, + isWarning: Boolean = false, onDismiss: (() -> Unit)? = null, - primaryAction: (() -> Unit)? = null + onClick: (() -> Unit)? = null ) { val color = if (isWarning) MaterialTheme.colorScheme.onError else MaterialTheme.colorScheme.onPrimaryContainer - NotificationCardInstance(isWarning = isWarning, onClick = primaryAction) { + NotificationCardInstance(modifier = modifier, isWarning = isWarning, onClick = onClick) { Row( modifier = Modifier .fillMaxWidth() @@ -102,12 +102,17 @@ fun NotificationCard( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp), ) { - Icon( - modifier = Modifier.size(if (title != null) 36.dp else 24.dp), - imageVector = icon, - contentDescription = null, - tint = color, - ) + Box( + modifier = Modifier.size(28.dp), + contentAlignment = Alignment.Center + ) { + Icon( + modifier = Modifier.size(24.dp), + imageVector = icon, + contentDescription = null, + tint = color, + ) + } if (title != null) { Column( modifier = Modifier.weight(1f), @@ -145,32 +150,31 @@ fun NotificationCard( } } -@OptIn(ExperimentalMaterial3Api::class) @Composable private fun NotificationCardInstance( + modifier: Modifier = Modifier, isWarning: Boolean = false, onClick: (() -> Unit)? = null, content: @Composable () -> Unit, ) { val colors = CardDefaults.cardColors(containerColor = if (isWarning) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primaryContainer) - val modifier = Modifier + val defaultModifier = Modifier .fillMaxWidth() - .padding(16.dp) .clip(RoundedCornerShape(24.dp)) if (onClick != null) { Card( onClick = onClick, colors = colors, - modifier = modifier + modifier = modifier.then(defaultModifier) ) { content() } } else { Card( colors = colors, - modifier = modifier, + modifier = modifier.then(defaultModifier) ) { content() } diff --git a/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleItem.kt b/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleItem.kt index 090e19ad..6cb13891 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleItem.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleItem.kt @@ -65,34 +65,22 @@ fun BundleItem( .height(64.dp) .fillMaxWidth() .combinedClickable( - onClick = { - viewBundleDialogPage = true - }, + onClick = { viewBundleDialogPage = true }, onLongClick = onSelect, ), - leadingContent = { - if(selectable) { + leadingContent = if (selectable) { + { Checkbox( checked = isBundleSelected, onCheckedChange = toggleSelection, ) } - }, + } else null, - headlineContent = { - Text( - text = bundle.name, - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurface - ) - }, + headlineContent = { Text(text = bundle.name) }, supportingContent = { state.patchBundleOrNull()?.patches?.size?.let { patchCount -> - Text( - text = pluralStringResource(R.plurals.patch_count, patchCount, patchCount), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) + Text(text = pluralStringResource(R.plurals.patch_count, patchCount, patchCount)) } }, trailingContent = { @@ -114,13 +102,7 @@ fun BundleItem( ) } - version?.let { txt -> - Text( - text = txt, - style = MaterialTheme.typography.labelSmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } + version?.let { Text(text = it) } } }, ) diff --git a/app/src/main/java/app/revanced/manager/ui/destination/SettingsDestination.kt b/app/src/main/java/app/revanced/manager/ui/destination/SettingsDestination.kt index 5b6e59ee..6f880de6 100644 --- a/app/src/main/java/app/revanced/manager/ui/destination/SettingsDestination.kt +++ b/app/src/main/java/app/revanced/manager/ui/destination/SettingsDestination.kt @@ -27,7 +27,7 @@ sealed interface SettingsDestination : Parcelable { object About : SettingsDestination @Parcelize - data class Update(val downloadOnScreenEntry: Boolean) : SettingsDestination + data class Update(val downloadOnScreenEntry: Boolean = false) : SettingsDestination @Parcelize object Changelogs : SettingsDestination diff --git a/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt index 2974e65a..03b0e26c 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt @@ -2,6 +2,7 @@ package app.revanced.manager.ui.screen import androidx.activity.compose.BackHandler import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize 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.Settings 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.FloatingActionButton import androidx.compose.material3.Icon @@ -24,6 +27,7 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Tab import androidx.compose.material3.TabRow import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -43,7 +47,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import app.revanced.manager.R import app.revanced.manager.data.room.apps.installed.InstalledApp 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.AutoUpdatesDialog +import app.revanced.manager.ui.component.NotificationCard import app.revanced.manager.ui.component.bundle.BundleItem import app.revanced.manager.ui.component.bundle.BundleTopBar import app.revanced.manager.ui.component.bundle.ImportBundleDialog @@ -68,38 +75,26 @@ fun DashboardScreen( vm: DashboardViewModel = koinViewModel(), onAppSelectorClick: () -> Unit, onSettingsClick: () -> Unit, + onUpdateClick: () -> 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 pages: Array = DashboardPage.values() val availablePatches by vm.availablePatches.collectAsStateWithLifecycle(0) val androidContext = LocalContext.current - + val composableScope = rememberCoroutineScope() val pagerState = rememberPagerState( initialPage = DashboardPage.DASHBOARD.ordinal, initialPageOffsetFraction = 0f - ) { - DashboardPage.values().size - } - val composableScope = rememberCoroutineScope() + ) { DashboardPage.entries.size } LaunchedEffect(pagerState.currentPage) { if (pagerState.currentPage != DashboardPage.BUNDLES.ordinal) vm.cancelSourceSelection() } - if (showBundleTypeSelectorDialog) { - ImportBundleTypeSelectorDialog( - onDismiss = { showBundleTypeSelectorDialog = false }, - onConfirm = { - selectedBundleType = it - showBundleTypeSelectorDialog = false - } - ) - } + val firstLaunch by vm.prefs.firstLaunch.getAsState() + if (firstLaunch) AutoUpdatesDialog(vm::applyAutoUpdatePrefs) + var selectedBundleType: BundleType? by rememberSaveable { mutableStateOf(null) } selectedBundleType?.let { fun dismiss() { 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( topBar = { 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 -> Column(Modifier.padding(paddingValues)) { @@ -202,7 +206,7 @@ fun DashboardScreen( selectedTabIndex = pagerState.currentPage, containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp) ) { - pages.forEachIndexed { index, page -> + DashboardPage.entries.forEachIndexed { index, page -> Tab( selected = pagerState.currentPage == 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( state = pagerState, userScrollEnabled = true, modifier = Modifier.fillMaxSize(), pageContent = { index -> - when (pages[index]) { + when (DashboardPage.entries[index]) { DashboardPage.DASHBOARD -> { InstalledAppsScreen( onAppClick = onAppClick @@ -238,11 +271,9 @@ fun DashboardScreen( val sources by vm.sources.collectAsStateWithLifecycle(initialValue = emptyList()) Column( - modifier = Modifier - .fillMaxSize(), + modifier = Modifier.fillMaxSize(), ) { sources.forEach { - BundleItem( bundle = it, 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() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/screen/InstalledAppsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/InstalledAppsScreen.kt index 06a9d7bd..264cf587 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/InstalledAppsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/InstalledAppsScreen.kt @@ -6,8 +6,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size 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.MaterialTheme import androidx.compose.material3.Text @@ -20,12 +18,10 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import app.revanced.manager.R 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.AppLabel import app.revanced.manager.ui.component.LazyColumnWithScrollbar import app.revanced.manager.ui.component.LoadingIndicator -import app.revanced.manager.ui.component.NotificationCard import app.revanced.manager.ui.viewmodel.InstalledAppsViewModel import org.koin.androidx.compose.koinViewModel @@ -37,16 +33,6 @@ fun InstalledAppsScreen( val installedApps by viewModel.apps.collectAsStateWithLifecycle(initialValue = null) Column { - if (!Aapt.supportsDevice()) { - NotificationCard( - isWarning = true, - icon = Icons.Outlined.WarningAmber, - text = stringResource( - R.string.unsupported_architecture_warning - ), - ) - } - LazyColumnWithScrollbar( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, diff --git a/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt index 382efa0e..08468808 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.setValue 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.R import app.revanced.manager.ui.component.AppTopBar import app.revanced.manager.ui.component.ColumnWithScrollbar @@ -161,10 +162,11 @@ fun SettingsScreen( ) { AnimatedVisibility(visible = showBatteryButton) { NotificationCard( + modifier = Modifier.padding(16.dp), isWarning = true, icon = Icons.Default.BatteryAlert, text = stringResource(R.string.battery_optimization_notification), - primaryAction = { + onClick = { context.startActivity(Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply { data = Uri.parse("package:${context.packageName}") }) diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt index 0d9c4dc0..22b83770 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt @@ -3,21 +3,32 @@ package app.revanced.manager.ui.viewmodel import android.app.Application import android.content.ContentResolver import android.net.Uri +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf +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.data.platform.NetworkInfo 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.manager.PreferencesManager 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.uiSafe +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch class DashboardViewModel( private val app: Application, - private val patchBundleRepository: PatchBundleRepository + private val patchBundleRepository: PatchBundleRepository, + private val reVancedAPI: ReVancedAPI, + private val networkInfo: NetworkInfo, + val prefs: PreferencesManager ) : ViewModel() { val availablePatches = patchBundleRepository.bundles.map { it.values.sumOf { bundle -> bundle.patches.size } } @@ -25,6 +36,47 @@ class DashboardViewModel( val sources = patchBundleRepository.sources val selectedSources = mutableStateListOf() + + 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() { selectedSources.clear() } diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/MainViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/MainViewModel.kt index 3a897ed4..b512749a 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/MainViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/MainViewModel.kt @@ -8,25 +8,18 @@ import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResult 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.viewModelScope 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.manager.KeystoreManager import app.revanced.manager.domain.manager.PreferencesManager import app.revanced.manager.domain.repository.PatchBundleRepository import app.revanced.manager.domain.repository.PatchSelectionRepository 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.util.isDebuggable import app.revanced.manager.util.tag import app.revanced.manager.util.toast -import app.revanced.manager.util.uiSafe import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.serialization.Serializable @@ -36,50 +29,9 @@ class MainViewModel( private val patchBundleRepository: PatchBundleRepository, private val patchSelectionRepository: PatchSelectionRepository, private val keystoreManager: KeystoreManager, - private val reVancedAPI: ReVancedAPI, private val app: Application, - private val networkInfo: NetworkInfo, val prefs: PreferencesManager ) : 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) { if (!prefs.firstLaunch.getBlocking()) return diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 09903146..97766c14 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -324,8 +324,7 @@ No update available Checking for updates… Not now - New update available - A new version (%s) is available for download. + A new version of ReVanced Manager (%s) is available. Failed to download update: %s Download You are currently on a metered connection, and data charges from your service provider may apply.\n\nDo you still want to continue?