Compare commits

..

3 Commits

Author SHA1 Message Date
ba1271b5b8 fix: Inconsistent string with context 2025-03-05 18:39:12 +03:00
9a9d1ea775 feat: Finish up design 2025-03-05 18:38:57 +03:00
690ab655f6 feat: Start redesign of trust dialog 2025-02-12 15:44:24 +03:00
17 changed files with 372 additions and 383 deletions

View File

@ -44,13 +44,14 @@ import app.revanced.manager.ui.screen.SettingsScreen
import app.revanced.manager.ui.screen.UpdateScreen
import app.revanced.manager.ui.screen.settings.AboutSettingsScreen
import app.revanced.manager.ui.screen.settings.AdvancedSettingsScreen
import app.revanced.manager.ui.screen.settings.BackupRestoreSettingsScreen
import app.revanced.manager.ui.screen.settings.ContributorScreen
import app.revanced.manager.ui.screen.settings.DeveloperOptionsScreen
import app.revanced.manager.ui.screen.settings.DownloadsSettingsScreen
import app.revanced.manager.ui.screen.settings.GeneralSettingsScreen
import app.revanced.manager.ui.screen.settings.ImportExportSettingsScreen
import app.revanced.manager.ui.screen.settings.LicensesScreen
import app.revanced.manager.ui.screen.settings.update.ChangelogsScreen
import app.revanced.manager.ui.screen.settings.update.UpdatesSettingsScreen
import app.revanced.manager.ui.theme.ReVancedManagerTheme
import app.revanced.manager.ui.theme.Theme
import app.revanced.manager.ui.viewmodel.MainViewModel
@ -269,28 +270,32 @@ private fun ReVancedManager(vm: MainViewModel) {
}
composable<Settings.General> {
GeneralSettingsScreen(
onBackClick = navController::popBackStack,
onUpdateClick = { navController.navigate(Update()) }
)
GeneralSettingsScreen(onBackClick = navController::popBackStack)
}
composable<Settings.Advanced> {
AdvancedSettingsScreen(onBackClick = navController::popBackStack)
}
composable<Settings.Updates> {
UpdatesSettingsScreen(
onBackClick = navController::popBackStack,
onChangelogClick = { navController.navigate(Settings.Changelogs) },
onUpdateClick = { navController.navigate(Update()) }
)
}
composable<Settings.Downloads> {
DownloadsSettingsScreen(onBackClick = navController::popBackStack)
}
composable<Settings.ImportExport> {
BackupRestoreSettingsScreen(onBackClick = navController::popBackStack)
ImportExportSettingsScreen(onBackClick = navController::popBackStack)
}
composable<Settings.About> {
AboutSettingsScreen(
onBackClick = navController::popBackStack,
onChangelogClick = { navController.navigate(Settings.Changelogs) },
navigate = navController::navigate
)
}

View File

@ -70,6 +70,9 @@ object Settings {
@Serializable
data object Advanced : Destination
@Serializable
data object Updates : Destination
@Serializable
data object Downloads : Destination

View File

@ -4,14 +4,8 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Download
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.SwapVert
import androidx.compose.material.icons.outlined.Tune
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@ -28,13 +22,18 @@ private val settingsSections = listOf(
Icons.Outlined.Settings
) to Settings.General,
Triple(
R.string.extensions,
R.string.extensions_description,
R.string.updates,
R.string.updates_description,
Icons.Outlined.Update
) to Settings.Updates,
Triple(
R.string.downloads,
R.string.downloads_description,
Icons.Outlined.Download
) to Settings.Downloads,
Triple(
R.string.backup_restore,
R.string.backup_restore_description,
R.string.import_export,
R.string.import_export_description,
Icons.Outlined.SwapVert
) to Settings.ImportExport,
Triple(

View File

@ -51,7 +51,6 @@ import org.koin.androidx.compose.koinViewModel
@Composable
fun AboutSettingsScreen(
onBackClick: () -> Unit,
onChangelogClick: () -> Unit,
navigate: (Settings.Destination) -> Unit,
viewModel: AboutViewModel = koinViewModel()
) {
@ -109,11 +108,6 @@ fun AboutSettingsScreen(
}
val listItems = listOfNotNull(
Triple(
stringResource(R.string.changelog),
stringResource(R.string.changelog_description),
third = { onChangelogClick }
),
Triple(stringResource(R.string.submit_feedback),
stringResource(R.string.submit_feedback_description),
third = {

View File

@ -17,7 +17,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Api
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material.icons.outlined.Restore
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
@ -101,10 +100,17 @@ fun AdvancedSettingsScreen(
defaultUrl = vm.prefs.api.default,
onSubmit = {
showApiUrlDialog = false
it?.let(vm::setApiSource)
it?.let(vm::setApiUrl)
}
)
}
SettingsListItem(
headlineContent = stringResource(R.string.api_url),
supportingContent = stringResource(R.string.api_url_description),
modifier = Modifier.clickable {
showApiUrlDialog = true
}
)
GroupHeader(stringResource(R.string.patcher))
BooleanItem(
@ -120,28 +126,13 @@ fun AdvancedSettingsScreen(
description = R.string.process_runtime_memory_limit_description,
)
GroupHeader(stringResource(R.string.manager))
SettingsListItem(
headlineContent = stringResource(R.string.api_url),
supportingContent = apiUrl,
modifier = Modifier.clickable {
showApiUrlDialog = true
},
trailingContent = {
IconButton(onClick = { showApiUrlDialog = true }) {
Icon(
Icons.Outlined.Edit,
contentDescription = stringResource(R.string.edit)
)
}
}
)
GroupHeader(stringResource(R.string.safeguards))
SafeguardBooleanItem(
preference = vm.prefs.disablePatchVersionCompatCheck,
coroutineScope = vm.viewModelScope,
headline = R.string.allow_compatibility_mixing,
description = R.string.allow_compatibility_mixing_description,
confirmationText = R.string.allow_compatibility_mixing_confirmation
headline = R.string.patch_compat_check,
description = R.string.patch_compat_check_description,
confirmationText = R.string.patch_compat_check_confirmation
)
SafeguardBooleanItem(
preference = vm.prefs.disableUniversalPatchWarning,
@ -150,6 +141,13 @@ fun AdvancedSettingsScreen(
description = R.string.universal_patches_safeguard_description,
confirmationText = R.string.universal_patches_safeguard_confirmation
)
SafeguardBooleanItem(
preference = vm.prefs.suggestedVersionSafeguard,
coroutineScope = vm.viewModelScope,
headline = R.string.suggested_version_safeguard,
description = R.string.suggested_version_safeguard_description,
confirmationText = R.string.suggested_version_safeguard_confirmation
)
SafeguardBooleanItem(
preference = vm.prefs.disableSelectionWarning,
coroutineScope = vm.viewModelScope,
@ -158,21 +156,6 @@ fun AdvancedSettingsScreen(
confirmationText = R.string.patch_selection_safeguard_confirmation
)
GroupHeader(stringResource(R.string.update))
BooleanItem(
preference = vm.prefs.showManagerUpdateDialogOnLaunch,
headline = R.string.show_manager_update_dialog_on_launch,
description = R.string.check_for_update_auto_description
)
GroupHeader(stringResource(R.string.experimental_features))
BooleanItem(
preference = vm.prefs.useProcessRuntime,
coroutineScope = vm.viewModelScope,
headline = R.string.process_runtime,
description = R.string.process_runtime_description,
)
GroupHeader(stringResource(R.string.debugging))
val exportDebugLogsLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) {

View File

@ -44,13 +44,7 @@ fun DeveloperOptionsScreen(
)
SettingsListItem(
headlineContent = stringResource(R.string.patch_bundles_reset),
modifier = Modifier.clickable(onClick = vm::resetBundles)
)
GroupHeader(stringResource(R.string.testing))
SettingsListItem(
headlineContent = stringResource(R.string.disable_safeguard),
modifier = Modifier.clickable(onClick = vm::disableSafeguard)
modifier = Modifier.clickable(onClick = vm::redownloadBundles)
)
}
}

View File

@ -2,7 +2,9 @@ package app.revanced.manager.ui.screen.settings
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@ -10,10 +12,13 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.AlertDialog
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
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@ -31,6 +36,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
@ -59,11 +65,12 @@ fun DownloadsSettingsScreen(
val downloadedApps by viewModel.downloadedApps.collectAsStateWithLifecycle(emptyList())
val pluginStates by viewModel.downloaderPluginStates.collectAsStateWithLifecycle()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val context = LocalContext.current
Scaffold(
topBar = {
AppTopBar(
title = stringResource(R.string.extensions),
title = stringResource(R.string.downloads),
scrollBehavior = scrollBehavior,
onBackClick = onBackClick,
actions = {
@ -129,15 +136,18 @@ fun DownloadsSettingsScreen(
.digest(androidSignature.toByteArray())
hash.toHexString(format = HexFormat.UpperCase)
}
val appName = packageInfo.applicationInfo?.loadLabel(context.packageManager)
?.toString()
?: packageName
when (state) {
is DownloaderPluginState.Loaded -> TrustDialog(
title = R.string.downloader_plugin_revoke_trust_dialog_title,
body = stringResource(
R.string.downloader_plugin_trust_dialog_body,
packageName,
signature
R.string.downloader_plugin_revoke_trust_dialog_body,
),
pluginName = appName,
signature = signature,
onDismiss = ::dismiss,
onConfirm = {
viewModel.revokePluginTrust(packageName)
@ -152,19 +162,20 @@ fun DownloadsSettingsScreen(
onDismiss = ::dismiss
)
is DownloaderPluginState.Untrusted -> TrustDialog(
title = R.string.downloader_plugin_trust_dialog_title,
body = stringResource(
R.string.downloader_plugin_trust_dialog_body,
packageName,
signature
),
onDismiss = ::dismiss,
onConfirm = {
viewModel.trustPlugin(packageName)
dismiss()
}
)
is DownloaderPluginState.Untrusted ->
TrustDialog(
title = R.string.downloader_plugin_trust_dialog_title,
body = stringResource(
R.string.downloader_plugin_trust_dialog_body
),
pluginName = appName,
signature = signature,
onDismiss = ::dismiss,
onConfirm = {
viewModel.trustPlugin(packageName)
dismiss()
}
)
}
}
@ -233,6 +244,8 @@ fun DownloadsSettingsScreen(
private fun TrustDialog(
@StringRes title: Int,
body: String,
pluginName: String,
signature: String,
onDismiss: () -> Unit,
onConfirm: () -> Unit
) {
@ -249,6 +262,39 @@ private fun TrustDialog(
}
},
title = { Text(stringResource(title)) },
text = { Text(body) }
text = {
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text(body)
Card(
colors = CardDefaults.outlinedCardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainer
)
) {
Column(
Modifier.padding(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
stringResource(
R.string.downloader_plugin_trust_dialog_plugin,
pluginName
),
)
OutlinedCard(
colors = CardDefaults.outlinedCardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh
)
) {
Text(
stringResource(
R.string.downloader_plugin_trust_dialog_signature,
signature.chunked(2).joinToString(" ")
), modifier = Modifier.padding(12.dp)
)
}
}
}
}
}
)
}

View File

@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Scaffold
@ -43,7 +42,6 @@ import org.koin.compose.koinInject
@Composable
fun GeneralSettingsScreen(
onBackClick: () -> Unit,
onUpdateClick: () -> Unit,
viewModel: GeneralSettingsViewModel = koinViewModel()
) {
val prefs = viewModel.prefs
@ -78,10 +76,10 @@ fun GeneralSettingsScreen(
val theme by prefs.theme.getAsState()
SettingsListItem(
modifier = Modifier.clickable { showThemePicker = true },
headlineContent = stringResource(R.string.theme_mode),
supportingContent = stringResource(R.string.theme_mode_description),
headlineContent = stringResource(R.string.theme),
supportingContent = stringResource(R.string.theme_description),
trailingContent = {
Button(
FilledTonalButton(
onClick = {
showThemePicker = true
}
@ -94,24 +92,11 @@ fun GeneralSettingsScreen(
BooleanItem(
preference = prefs.dynamicColor,
coroutineScope = coroutineScope,
headline = R.string.personalized_color,
description = R.string.personalized_color_description
headline = R.string.dynamic_color,
description = R.string.dynamic_color_description
)
}
}
GroupHeader(stringResource(R.string.update))
BooleanItem(
preference = prefs.managerAutoUpdates,
headline = R.string.check_for_update,
description = R.string.check_for_update_auto_description
)
FilledTonalButton (
modifier = Modifier.padding(top = paddingValues.calculateTopPadding()),
onClick = onUpdateClick
) {
Text(stringResource(R.string.check_for_update))
}
}
}
@ -125,7 +110,7 @@ private fun ThemePicker(
AlertDialog(
onDismissRequest = onDismiss,
title = { Text(stringResource(R.string.theme_mode)) },
title = { Text(stringResource(R.string.theme)) },
text = {
Column {
Theme.entries.forEach {

View File

@ -54,53 +54,53 @@ import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BackupRestoreSettingsScreen(
fun ImportExportSettingsScreen(
onBackClick: () -> Unit,
viewModel: ImportExportViewModel = koinViewModel()
vm: ImportExportViewModel = koinViewModel()
) {
val context = LocalContext.current
val importKeystoreLauncher =
rememberLauncherForActivityResult(contract = ActivityResultContracts.GetContent()) {
it?.let { uri -> viewModel.startKeystoreImport(uri) }
it?.let { uri -> vm.startKeystoreImport(uri) }
}
val exportKeystoreLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("*/*")) {
it?.let(viewModel::exportKeystore)
it?.let(vm::exportKeystore)
}
val patchBundles by viewModel.patchBundles.collectAsStateWithLifecycle(initialValue = emptyList())
val packagesWithOptions by viewModel.packagesWithOptions.collectAsStateWithLifecycle(initialValue = emptySet())
val patchBundles by vm.patchBundles.collectAsStateWithLifecycle(initialValue = emptyList())
val packagesWithOptions by vm.packagesWithOptions.collectAsStateWithLifecycle(initialValue = emptySet())
viewModel.selectionAction?.let { action ->
vm.selectionAction?.let { action ->
val launcher = rememberLauncherForActivityResult(action.activityContract) { uri ->
if (uri == null) {
viewModel.clearSelectionAction()
vm.clearSelectionAction()
} else {
viewModel.executeSelectionAction(uri)
vm.executeSelectionAction(uri)
}
}
if (viewModel.selectedBundle == null) {
if (vm.selectedBundle == null) {
BundleSelector(patchBundles) {
if (it == null) {
viewModel.clearSelectionAction()
vm.clearSelectionAction()
} else {
viewModel.selectBundle(it)
vm.selectBundle(it)
launcher.launch(action.activityArg)
}
}
}
}
if (viewModel.showCredentialsDialog) {
if (vm.showCredentialsDialog) {
KeystoreCredentialsDialog(
onDismissRequest = viewModel::cancelKeystoreImport,
onDismissRequest = vm::cancelKeystoreImport,
onSubmit = { cn, pass ->
viewModel.viewModelScope.launch {
vm.viewModelScope.launch {
uiSafe(context, R.string.failed_to_import_keystore, "Failed to import keystore") {
val result = viewModel.tryKeystoreImport(cn, pass)
if (!result) context.toast(context.getString(R.string.restore_keystore_wrong_credentials))
val result = vm.tryKeystoreImport(cn, pass)
if (!result) context.toast(context.getString(R.string.import_keystore_wrong_credentials))
}
}
}
@ -112,7 +112,7 @@ fun BackupRestoreSettingsScreen(
Scaffold(
topBar = {
AppTopBar(
title = stringResource(R.string.backup_restore),
title = stringResource(R.string.import_export),
scrollBehavior = scrollBehavior,
onBackClick = onBackClick
)
@ -133,7 +133,7 @@ fun BackupRestoreSettingsScreen(
if (showPackageSelector) {
PackageSelector(packages = packagesWithOptions) { selected ->
selected?.let(viewModel::resetOptionsForPackage)
selected?.let(vm::resetOptionsForPackage)
showPackageSelector = false
}
@ -141,95 +141,69 @@ fun BackupRestoreSettingsScreen(
if (showBundleSelector) {
BundleSelector(bundles = patchBundles) { bundle ->
bundle?.let(viewModel::clearOptionsForBundle)
bundle?.let(vm::clearOptionsForBundle)
showBundleSelector = false
}
}
GroupHeader(stringResource(R.string.keystore))
GroupItem(
onClick = {
if (!viewModel.canExport()) {
context.toast(context.getString(R.string.backup_keystore_unavailable))
return@GroupItem
}
exportKeystoreLauncher.launch("Manager.keystore")
},
headline = R.string.backup,
description = R.string.backup_keystore_description
)
GroupHeader(stringResource(R.string.import_))
GroupItem(
onClick = {
importKeystoreLauncher.launch("*/*")
},
headline = R.string.restore,
description = R.string.restore_keystore_description
headline = R.string.import_keystore,
description = R.string.import_keystore_description
)
GroupItem(
onClick = viewModel::regenerateKeystore,
headline = {
Text(
stringResource(R.string.regenerate_keystore),
color = MaterialTheme.colorScheme.error
)
onClick = vm::importSelection,
headline = R.string.import_patch_selection,
description = R.string.import_patch_selection_description
)
GroupHeader(stringResource(R.string.export))
GroupItem(
onClick = {
if (!vm.canExport()) {
context.toast(context.getString(R.string.export_keystore_unavailable))
return@GroupItem
}
exportKeystoreLauncher.launch("Manager.keystore")
},
headline = R.string.export_keystore,
description = R.string.export_keystore_description
)
GroupItem(
onClick = vm::exportSelection,
headline = R.string.export_patch_selection,
description = R.string.export_patch_selection_description
)
GroupHeader(stringResource(R.string.reset))
GroupItem(
onClick = vm::regenerateKeystore,
headline = R.string.regenerate_keystore,
description = R.string.regenerate_keystore_description
)
GroupHeader(stringResource(R.string.patch_selection))
GroupItem(
onClick = viewModel::exportSelection,
headline = R.string.backup,
description = R.string.restore_patch_selection_description
)
GroupItem(
onClick = viewModel::importSelection,
headline = R.string.restore,
description = R.string.backup_patch_selection_description
)
GroupItem(
onClick = viewModel::resetSelection, // TODO: allow resetting selection for specific bundle or package name.
headline = {
Text(
stringResource(R.string.reset),
color = MaterialTheme.colorScheme.error
)
},
onClick = vm::resetSelection, // TODO: allow resetting selection for specific bundle or package name.
headline = R.string.reset_patch_selection,
description = R.string.reset_patch_selection_description
)
GroupHeader(stringResource(R.string.patch_options))
// TODO: patch options import/export.
GroupItem(
onClick = viewModel::resetOptions,
headline = {
Text(
stringResource(R.string.reset),
color = MaterialTheme.colorScheme.error
)
},
onClick = vm::resetOptions, // TODO: patch options import/export.
headline = R.string.patch_options_reset_all,
description = R.string.patch_options_reset_all_description,
)
GroupItem(
onClick = { showPackageSelector = true },
headline = {
Text(
stringResource(R.string.patch_options_reset_package),
color = MaterialTheme.colorScheme.error
)
},
headline = R.string.patch_options_reset_package,
description = R.string.patch_options_reset_package_description
)
if (patchBundles.size > 1) {
GroupItem(
onClick = { showBundleSelector = true },
headline = {
Text(
stringResource(R.string.patch_options_reset_bundle),
color = MaterialTheme.colorScheme.error
)
},
headline = R.string.patch_options_reset_bundle,
description = R.string.patch_options_reset_bundle_description,
)
}
@ -308,19 +282,6 @@ private fun GroupItem(
)
}
@Composable
private fun GroupItem(
onClick: () -> Unit,
headline: @Composable () -> Unit,
@StringRes description: Int? = null
) {
SettingsListItem(
modifier = Modifier.clickable { onClick() },
headlineContent = headline,
supportingContent = description?.let { stringResource(it) }
)
}
@Composable
fun KeystoreCredentialsDialog(
onDismissRequest: () -> Unit,
@ -337,7 +298,7 @@ fun KeystoreCredentialsDialog(
onSubmit(cn, pass)
}
) {
Text(stringResource(R.string.restore_keystore_dialog_button))
Text(stringResource(R.string.import_keystore_dialog_button))
}
},
dismissButton = {
@ -350,7 +311,7 @@ fun KeystoreCredentialsDialog(
},
title = {
Text(
text = stringResource(R.string.restore_keystore_dialog_title),
text = stringResource(R.string.import_keystore_dialog_title),
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center),
color = MaterialTheme.colorScheme.onSurface,
)
@ -361,19 +322,19 @@ fun KeystoreCredentialsDialog(
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
text = stringResource(R.string.restore_keystore_dialog_description),
text = stringResource(R.string.import_keystore_dialog_description),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
OutlinedTextField(
value = cn,
onValueChange = { cn = it },
label = { Text(stringResource(R.string.restore_keystore_dialog_alias_field)) }
label = { Text(stringResource(R.string.import_keystore_dialog_alias_field)) }
)
PasswordField(
value = pass,
onValueChange = { pass = it },
label = { Text(stringResource(R.string.restore_keystore_dialog_password_field)) }
label = { Text(stringResource(R.string.import_keystore_dialog_password_field)) }
)
}
}

View File

@ -0,0 +1,81 @@
package app.revanced.manager.ui.screen.settings.update
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource
import app.revanced.manager.R
import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.ColumnWithScrollbar
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.koinViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun UpdatesSettingsScreen(
onBackClick: () -> Unit,
onChangelogClick: () -> Unit,
onUpdateClick: () -> Unit,
vm: UpdatesSettingsViewModel = koinViewModel(),
) {
val coroutineScope = rememberCoroutineScope()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
Scaffold(
topBar = {
AppTopBar(
title = stringResource(R.string.updates),
scrollBehavior = scrollBehavior,
onBackClick = onBackClick
)
},
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
) { paddingValues ->
ColumnWithScrollbar(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
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)
)
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
)
BooleanItem(
preference = vm.showManagerUpdateDialogOnLaunch,
headline = R.string.show_manager_update_dialog_on_launch,
description = R.string.update_checking_manager_description
)
}
}
}

View File

@ -34,7 +34,7 @@ class AdvancedSettingsViewModel(
return "revanced-manager_logcat_$time"
}
fun setApiSource(value: String) = viewModelScope.launch(Dispatchers.Default) {
fun setApiUrl(value: String) = viewModelScope.launch(Dispatchers.Default) {
if (value == prefs.api.get()) return@launch
prefs.api.update(value)

View File

@ -24,10 +24,4 @@ class DeveloperOptionsViewModel(
fun resetBundles() = viewModelScope.launch {
patchBundleRepository.reset()
}
fun disableSafeguard() = viewModelScope.launch {
prefs.disablePatchVersionCompatCheck.update(true)
prefs.disableSelectionWarning.update(true)
prefs.disableUniversalPatchWarning.update(true)
}
}

View File

@ -103,7 +103,7 @@ class ImportExportViewModel(
private suspend fun tryKeystoreImport(cn: String, pass: String, path: Path): Boolean {
path.inputStream().use { stream ->
if (keystoreManager.import(cn, pass, stream)) {
app.toast(app.getString(R.string.restore_keystore_success))
app.toast(app.getString(R.string.import_keystore_success))
cancelKeystoreImport()
return true
}
@ -122,7 +122,7 @@ class ImportExportViewModel(
fun exportKeystore(target: Uri) = viewModelScope.launch {
keystoreManager.export(contentResolver.openOutputStream(target)!!)
app.toast(app.getString(R.string.backup_keystore_success))
app.toast(app.getString(R.string.export_keystore_success))
}
fun regenerateKeystore() = viewModelScope.launch {
@ -171,7 +171,7 @@ class ImportExportViewModel(
override val activityArg = JSON_MIMETYPE
override suspend fun execute(bundleUid: Int, location: Uri) = uiSafe(
app,
R.string.restore_patch_selection_fail,
R.string.import_patch_selection_fail,
"Failed to restore patch selection"
) {
val selection = withContext(Dispatchers.IO) {
@ -181,7 +181,7 @@ class ImportExportViewModel(
}
selectionRepository.import(bundleUid, selection)
app.toast(app.getString(R.string.restore_patch_selection_success))
app.toast(app.getString(R.string.import_patch_selection_success))
}
}
@ -190,7 +190,7 @@ class ImportExportViewModel(
override val activityArg = "selection.json"
override suspend fun execute(bundleUid: Int, location: Uri) = uiSafe(
app,
R.string.backup_patch_selection_fail,
R.string.export_patch_selection_fail,
"Failed to backup patch selection"
) {
val selection = selectionRepository.export(bundleUid)
@ -200,7 +200,7 @@ class ImportExportViewModel(
Json.Default.encodeToStream(selection, it)
}
}
app.toast(app.getString(R.string.backup_patch_selection_success))
app.toast(app.getString(R.string.export_patch_selection_success))
}
}

View File

@ -137,8 +137,8 @@ class MainViewModel(
updateCheck()
}
}
settings.patchesChangeEnabled?.let { allowChangingPatchSelection ->
prefs.allowChangingPatchSelection.update(allowChangingPatchSelection)
settings.patchesChangeEnabled?.let { disableSelectionWarning ->
prefs.disableSelectionWarning.update(disableSelectionWarning)
}
settings.keystore?.let { keystore ->
val keystoreBytes = Base64.decode(keystore, Base64.DEFAULT)

View File

@ -66,7 +66,7 @@ class PatchesSelectorViewModel(input: SelectedApplicationInfo.PatchesSelector.Vi
viewModelScope.launch {
universalPatchWarningEnabled = !prefs.disableUniversalPatchWarning.get()
if (prefs.allowChangingPatchSelection.get()) {
if (prefs.disableSelectionWarning.get()) {
selectionWarningEnabled = false
return@launch
}

View File

@ -152,7 +152,7 @@ class SelectedAppInfoViewModel(
// Try to get the previous selection if customization is enabled.
viewModelScope.launch {
if (!prefs.allowChangingPatchSelection.get()) return@launch
if (!prefs.disableSelectionWarning.get()) return@launch
val previous = selectionRepository.getSelection(packageName)
if (previous.values.sumOf { it.size } == 0) return@launch

View File

@ -5,11 +5,8 @@
<string name="cli">CLI</string>
<string name="manager">Manager</string>
<string name="revanced_patcher">ReVanced Patcher</string>
<string name="plugin_host_permission_label">ReVanced Manager plugin host</string>
<string name="plugin_host_permission_description">Used to control access to ReVanced Manager
plugins. Only ReVanced Manager has this.</string>
<string name="plugin_host_permission_description">Used to control access to ReVanced Manager plugins. Only ReVanced Manager has this.</string>
<string name="toast_copied_to_clipboard">Copied!</string>
<string name="copy_to_clipboard">Copy to clipboard</string>
@ -19,10 +16,8 @@
<string name="select_app">Select an app</string>
<string name="patches_selected">%1$d/%2$d selected</string>
<string name="new_downloader_plugins_notification">New downloader plugins available. Click here
to configure them.</string>
<string name="unsupported_architecture_warning">Patching on this device architecture is
unsupported and will most likely fail.</string>
<string name="new_downloader_plugins_notification">New downloader plugins available. Click here to configure them.</string>
<string name="unsupported_architecture_warning">Patching on this device architecture is unsupported and will most likely fail.</string>
<string name="import_">Import</string>
<string name="import_bundle">Import patch bundle</string>
@ -41,20 +36,15 @@
<string name="bundle_name_fallback">Unnamed</string>
<string name="android_11_bug_dialog_title">Android 11 bug</string>
<string name="android_11_bug_dialog_description">The app installation permission must be granted
ahead of time to avoid a bug in the Android 11 system that will negatively affect the user
experience.</string>
<string name="android_11_bug_dialog_description">The app installation permission must be granted ahead of time to avoid a bug in the Android 11 system that will negatively affect the user experience.</string>
<string name="selected_app_meta_any_version">Any available version</string>
<string name="app_source_dialog_title">Select source</string>
<string name="app_source_dialog_option_auto">Auto</string>
<string name="app_source_dialog_option_auto_description">Use all installed downloaders to find a
suitable APK file</string>
<string name="app_source_dialog_option_auto_description">Use all installed downloaders to find a suitable APK file</string>
<string name="app_source_dialog_option_auto_unavailable">No plugins available</string>
<string name="app_source_dialog_option_installed_no_root">Mounted apps cannot be patched again
without root access</string>
<string name="app_source_dialog_option_installed_version_not_suggested">Version %s does not
match the suggested version</string>
<string name="app_source_dialog_option_installed_no_root">Mounted apps cannot be patched again without root access</string>
<string name="app_source_dialog_option_installed_version_not_suggested">Version %s does not match the suggested version</string>
<string name="patch_item_description">Start patching the application</string>
<string name="patch_selector_item">Patch selection and options</string>
@ -70,98 +60,87 @@
<string name="legacy_import_failed">Could not import legacy settings</string>
<string name="auto_updates_dialog_title">Configure updates</string>
<string name="auto_updates_dialog_description">Do you want ReVanced Manager to periodically
check for updates for the following components?</string>
<string name="auto_updates_dialog_description">Do you want ReVanced Manager to periodically check for updates for the following components?</string>
<string name="auto_updates_dialog_manager">ReVanced Manager</string>
<string name="auto_updates_dialog_patches">ReVanced Patches</string>
<string name="auto_updates_dialog_note">These settings can be changed later.</string>
<string name="general">General</string>
<string name="general_description">Appearances, Updates</string>
<string name="general_description">Theme, dynamic color</string>
<string name="updates">Updates</string>
<string name="updates_description">Check for updates and view changelogs</string>
<string name="extensions">Extensions</string>
<string name="extensions_description">Downloader plugins, downloaded apps</string>
<string name="backup_restore">Backup &amp; Restore</string>
<string name="backup_restore_description">Keystore, Patch selections, Patch options</string>
<string name="downloads">Downloads</string>
<string name="downloads_description">Downloader plugins and downloaded apps</string>
<string name="import_export">Import &amp; export</string>
<string name="import_export_description">Keystore, patch options and selection</string>
<string name="advanced">Advanced</string>
<string name="advanced_description">API Source, memory limits, debug logs</string>
<string name="experimental_features">Experimental features</string>
<string name="advanced_description">API URL, memory limit, debugging</string>
<string name="about">About</string>
<string name="opensource_licenses">Open source licenses</string>
<string name="opensource_licenses_description">View all the libraries used to make this
application</string>
<string name="opensource_licenses_description">View all the libraries used to make this application</string>
<string name="contributors">Contributors</string>
<string name="contributors_description">View the contributors of ReVanced</string>
<string name="personalized_color">Personalized color</string>
<string name="personalized_color_description">Use color provided by your device\'s palette</string>
<string name="theme_mode">Theme mode</string>
<string name="theme_mode_description">Choose between light, dark, and system provided mode</string>
<string name="dynamic_color">Dynamic color</string>
<string name="dynamic_color_description">Adapt colors to the wallpaper</string>
<string name="theme">Theme</string>
<string name="theme_description">Choose between light or dark theme</string>
<string name="safeguards">Safeguards</string>
<string name="allow_compatibility_mixing">Allow unsupported compatibility</string>
<string name="allow_compatibility_mixing_description">Permit apps and patches to be mixed in
unsupported state</string>
<string name="allow_compatibility_mixing_confirmation">Selecting incompatible patches can result
in a broken app.\n\nAllow anyways?</string>
<string name="patch_compat_check">Disable version compatibility check</string>
<string name="patch_compat_check_description">The check restricts patches to supported app versions</string>
<string name="patch_compat_check_confirmation">Selecting incompatible patches can result in a broken app.\n\nDo you want to proceed anyways?</string>
<string name="suggested_version_safeguard">Require suggested app version</string>
<string name="suggested_version_safeguard_description">Enforce selection of the suggested app version</string>
<string name="suggested_version_safeguard_confirmation">Selecting an app that is not the suggested version may cause unexpected issues.\n\nDo you want to proceed anyways?</string>
<string name="patch_selection_safeguard">Allow changing patch selection</string>
<string name="patch_selection_safeguard_description">Permit selecting or deselecting patches
from default</string>
<string name="patch_selection_safeguard_confirmation">Changing the selection of patches may
cause unexpected issues.\n\nAllow anyways?</string>
<string name="universal_patches_safeguard">Allow universal patch</string>
<string name="universal_patches_safeguard_description">Permit selecting app\'s generic patch</string>
<string name="universal_patches_safeguard_confirmation">Universal patch are not as well tested
as those that target specific apps.\n\nAllow anyways?</string>
<string name="backup">Backup</string>
<string name="restore">Restore</string>
<string name="keystore">Keystore</string>
<string name="patch_selection">Patch selection</string>
<string name="patch_options">Patch options</string>
<string name="restore_keystore_description">Restore keystore from external source</string>
<string name="restore_keystore_dialog_title">Enter keystore credentials</string>
<string name="restore_keystore_dialog_description">You\'ll need enter the keystores credentials
to restore it.</string>
<string name="restore_keystore_dialog_alias_field">Username (Alias)</string>
<string name="restore_keystore_dialog_password_field">Password</string>
<string name="restore_keystore_dialog_button">Import</string>
<string name="restore_keystore_wrong_credentials">Wrong keystore credentials</string>
<string name="restore_keystore_success">Keystore successfully restored</string>
<string name="backup_keystore_description">Export apps keystore into usable file</string>
<string name="backup_keystore_unavailable">No keystore to backup</string>
<string name="backup_keystore_success">Keystore successfully backed up</string>
<string name="regenerate_keystore">Regeneration</string>
<string name="regenerate_keystore_description">Replace current keystore with new one</string>
<string name="patch_selection_safeguard_description">Do not prevent selecting or deselecting patches</string>
<string name="patch_selection_safeguard_confirmation">Changing the selection of patches may cause unexpected issues.\n\nEnable anyways?</string>
<string name="universal_patches_safeguard">Disable universal patch warning</string>
<string name="universal_patches_safeguard_description">Disables the warning that appears when you try to select universal patches</string>
<string name="universal_patches_safeguard_confirmation">Universal patches are not as well tested as those that target specific apps.\n\nEnable anyways?</string>
<string name="import_keystore">Import keystore</string>
<string name="import_keystore_description">Import a custom keystore</string>
<string name="import_keystore_dialog_title">Enter keystore credentials</string>
<string name="import_keystore_dialog_description">You\'ll need enter the keystores credentials to import it.</string>
<string name="import_keystore_dialog_alias_field">Username (Alias)</string>
<string name="import_keystore_dialog_password_field">Password</string>
<string name="import_keystore_dialog_button">Import</string>
<string name="import_keystore_wrong_credentials">Wrong keystore credentials</string>
<string name="import_keystore_success">Imported keystore</string>
<string name="export_keystore">Export keystore</string>
<string name="export_keystore_description">Export the current keystore</string>
<string name="export_keystore_unavailable">No keystore to export</string>
<string name="export_keystore_success">Exported keystore</string>
<string name="regenerate_keystore">Regenerate keystore</string>
<string name="regenerate_keystore_description">Generate a new keystore</string>
<string name="regenerate_keystore_success">The keystore has been successfully replaced</string>
<string name="restore_patch_selection_description">Export apps patch selections into usable
file</string>
<string name="restore_patch_selection_fail">Could not import patch selection: %s</string>
<string name="restore_patch_selection_success">Patch selection successfully restored</string>
<string name="backup_patch_selection_description">Import apps patch selections from external
source</string>
<string name="backup_patch_selection_fail">Could not backup patch selection: %s</string>
<string name="backup_patch_selection_success">Patch selection successfully restored</string>
<string name="import_patch_selection">Import patch selection</string>
<string name="import_patch_selection_description">Import patch selection from a JSON file</string>
<string name="import_patch_selection_fail">Could not import patch selection: %s</string>
<string name="import_patch_selection_success">Imported patch selection</string>
<string name="export_patch_selection">Export patch selection</string>
<string name="export_patch_selection_description">Export patch selection to a JSON file</string>
<string name="export_patch_selection_fail">Could not export patch selection: %s</string>
<string name="export_patch_selection_success">Exported patch selection</string>
<string name="reset_patch_selection">Reset patch selection</string>
<string name="reset_patch_selection_description">Reset the stored patch selection</string>
<string name="reset_patch_selection_success">Patch selection successfully reset</string>
<string name="patch_options_reset_package">Reset for app</string>
<string name="patch_options_reset_package_description">Use the default patch options
configuration for a single app</string>
<string name="patch_options_reset_bundle">Reset for bundle</string>
<string name="patch_options_reset_bundle_description">Use the default patch options
configuration for all patches in a bundle</string>
<string name="patch_options_reset_all_description">Use the default patch options configuration</string>
<string name="reset_patch_selection_success">Patch selection has been reset</string>
<string name="patch_options_reset_package">Reset patch options for app</string>
<string name="patch_options_reset_package_description">Resets patch options for a single app</string>
<string name="patch_options_reset_bundle">Resets patch options for bundle</string>
<string name="patch_options_reset_bundle_description">Resets patch options for all patches in a bundle</string>
<string name="patch_options_reset_all">Reset patch options</string>
<string name="patch_options_reset_all_description">Resets all patch options</string>
<string name="downloader_plugins">Plugins</string>
<string name="downloader_plugin_state_trusted">Trusted</string>
<string name="downloader_plugin_state_failed">Failed to load. Click for more details</string>
<string name="downloader_plugin_state_untrusted">Untrusted</string>
<string name="downloader_plugin_trust_dialog_title">Trust plugin?</string>
<string name="downloader_plugin_revoke_trust_dialog_title">Revoke trust?</string>
<string name="downloader_plugin_trust_dialog_body">Package name: %1$s\nSignature (SHA-256): %2$s</string>
<string name="downloader_plugin_trust_dialog_body">By continuing you\'ve agreed to running external plugins.\n\nDo not allow any suspicious plugin(s) to be installed as they can run arbitrary code.</string>
<string name="downloader_plugin_revoke_trust_dialog_body">By revoking trust, this plugin will be disabled and prevented from running.</string>
<string name="downloader_plugin_trust_dialog_signature">Signature:\n\n%s</string>
<string name="downloader_plugin_trust_dialog_plugin">Plugin:\n%s</string>
<string name="downloader_settings_no_apps">No downloaded apps found</string>
<string name="search_apps">Search apps…</string>
@ -194,25 +173,21 @@
<string name="dark">Dark</string>
<string name="appearance">Appearance</string>
<string name="downloaded_apps">Downloaded apps</string>
<string name="process_runtime">Run patcher in another process</string>
<string name="process_runtime_description">Faster and allows patcher to use more memory</string>
<string name="process_runtime">Run Patcher in another process (experimental)</string>
<string name="process_runtime_description">This is faster and allows Patcher to use more memory.</string>
<string name="process_runtime_memory_limit">Patcher process memory limit</string>
<string name="process_runtime_memory_limit_description">The max amount of memory that the
patcher process can use (in megabytes)</string>
<string name="process_runtime_memory_limit_description">The max amount of memory that the Patcher process can use (in megabytes)</string>
<string name="debug_logs_export">Export debug logs</string>
<string name="debug_logs_export_read_failed">Failed to read logs (exit code %d)</string>
<string name="debug_logs_export_failed">Failed to export logs</string>
<string name="debug_logs_export_success">Exported logs</string>
<string name="api_url">API Source</string>
<string name="api_url_dialog_title">Set custom API Source</string>
<string name="api_url_dialog_description">Set the API Source of ReVanced Manager. ReVanced
Manager uses the API to download patches and updates.</string>
<string name="api_url_dialog_warning">ReVanced Manager connects to the API to download patches
and updates. Make sure that you trust it.</string>
<string name="api_url">API URL</string>
<string name="api_url_description">The API used to download necessary files.</string>
<string name="api_url_dialog_title">Set custom API URL</string>
<string name="api_url_dialog_description">Set the API URL of ReVanced Manager. ReVanced Manager uses the API to download patches and updates.</string>
<string name="api_url_dialog_warning">ReVanced Manager connects to the API to download patches and updates. Make sure that you trust it.</string>
<string name="api_url_dialog_save">Set</string>
<string name="api_url_dialog_reset">Reset API Source</string>
<string name="testing">Testing</string>
<string name="disable_safeguard">Disable all safeguards</string>
<string name="api_url_dialog_reset">Reset API URL</string>
<string name="device">Device</string>
<string name="device_android_version">Android version</string>
<string name="device_model">Model</string>
@ -241,32 +216,22 @@
<string name="bundles_selected">%s selected</string>
<string name="unsupported_patches">Incompatible patches</string>
<string name="universal_patches">Universal patches</string>
<string name="patch_selection_reset_toast">Patch selection and options has been reset to
recommended defaults</string>
<string name="patch_selection_reset_toast">Patch selection and options has been reset to recommended defaults</string>
<string name="patch_options_reset_toast">Patch options have been reset</string>
<string name="non_suggested_version_warning_title">Non suggested version</string>
<string name="non_suggested_version_warning_description">The version of the app you have
selected does not match the suggested version.\nPlease use the suggested version: %s\n\nTo
continue anyway, disable \"Require suggested app version\" in the advanced settings.</string>
<string name="non_suggested_version_warning_description">The version of the app you have selected does not match the suggested version.\nPlease use the suggested version: %s\n\nTo continue anyway, disable \"Require suggested app version\" in the advanced settings.</string>
<string name="selection_warning_title">Stop using defaults?</string>
<string name="selection_warning_description">It is recommended to use the default patch
selection and options. Changing them may result in unexpected issues.\n\nYou need to turn on
\"Allow changing patch selection\" in the advanced settings before toggling patches.</string>
<string name="universal_patch_warning_description">Universal patches have a more generalized use
and do not work as reliably as patches that target specific apps. You may encounter issues
while using them.\n\nThis warning can be disabled in the advanced settings.</string>
<string name="selection_warning_description">It is recommended to use the default patch selection and options. Changing them may result in unexpected issues.\n\nYou need to turn on \"Allow changing patch selection\" in the advanced settings before toggling patches.</string>
<string name="universal_patch_warning_description">Universal patches have a more generalized use and do not work as reliably as patches that target specific apps. You may encounter issues while using them.\n\nThis warning can be disabled in the advanced settings.</string>
<string name="supported">This version</string>
<string name="universal">Any app</string>
<string name="unsupported">Unsupported</string>
<string name="search_patches">Search patches</string>
<string name="app_not_supported">This patch is not compatible with the selected app version
(%1$s).\n\nIt only supports the following version(s): %2$s.</string>
<string name="app_not_supported">This patch is not compatible with the selected app version (%1$s).\n\nIt only supports the following version(s): %2$s.</string>
<string name="continue_with_version">Continue with this version?</string>
<string name="version_not_supported">Not all patches support this version (%s). Do you want to
continue anyway?</string>
<string name="version_not_supported">Not all patches support this version (%s). Do you want to continue anyway?</string>
<string name="download_application">Download application?</string>
<string name="app_not_installed">The app you selected isn\'t installed. Do you want to download
it?</string>
<string name="app_not_installed">The app you selected isn\'t installed. Do you want to download it?</string>
<string name="failed_to_load_apk">Failed to load APK</string>
<string name="loading">Loading…</string>
<string name="not_installed">Not installed</string>
@ -296,8 +261,7 @@
<string name="downloader_app_not_found">Downloader did not find the app</string>
<string name="downloader_error">Downloader error: %s</string>
<string name="downloader_no_plugins_installed">No plugins installed.</string>
<string name="downloader_no_plugins_available">No trusted plugins available for use. Check your
settings.</string>
<string name="downloader_no_plugins_available">No trusted plugins available for use. Check your settings.</string>
<string name="already_patched">Already patched</string>
<string name="patch_selector_sheet_filter_title">Filter</string>
@ -325,8 +289,7 @@
<string name="save_apk_success">APK Saved</string>
<string name="sign_fail">Failed to sign APK: %s</string>
<string name="save_logs">Save logs</string>
<string name="plugin_activity_dialog_body">User interaction is required in order to proceed with
this plugin.</string>
<string name="plugin_activity_dialog_body">User interaction is required in order to proceed with this plugin.</string>
<string name="select_install_type">Select installation type</string>
<string name="patcher_step_group_preparing">Preparing</string>
@ -368,33 +331,28 @@
<string name="bundle_update_success">Successfully updated %s</string>
<string name="bundle_update_unavailable">No update available for %s</string>
<string name="bundle_auto_update">Auto update</string>
<string name="bundle_auto_update_description">Automatically update this bundle when ReVanced
starts</string>
<string name="bundle_auto_update_description">Automatically update this bundle when ReVanced starts</string>
<string name="bundle_view_patches">View patches</string>
<string name="bundle_view_patches_any_version">Any version</string>
<string name="bundle_view_patches_any_package">Any package</string>
<string name="about_revanced_manager">About ReVanced Manager</string>
<string name="revanced_manager_description">ReVanced Manager is an application designed to work
with ReVanced Patcher, which allows for long-lasting patches to be created for Android apps.
The patching system is designed to automatically work with new versions of apps with minimal
maintenance.</string>
<string name="revanced_manager_description">ReVanced Manager is an application designed to work with ReVanced Patcher, which allows for long-lasting patches to be created for Android apps. The patching system is designed to automatically work with new versions of apps with minimal maintenance.</string>
<string name="update_available">An update is available</string>
<string name="current_version">Current version: %s</string>
<string name="new_version">New version: %s</string>
<string name="ready_to_install_update">Ready to install update</string>
<string name="update_completed">Update installed</string>
<string name="install_update_manager_failed">Failed to install update</string>
<string name="check_for_update">Check for update</string>
<string name="check_for_update_auto_description">Automatically check for new version when the
app launched</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">Auto check for updates</string>
<string name="update_checking_manager_description">Check for new versions of ReVanced Manager when the application starts</string>
<string name="changelog">View changelogs</string>
<string name="changelog_loading">Loading changelog</string>
<string name="changelog_download_fail">Failed to download changelog: %s</string>
<string name="changelog_description">Check out the latest changes in this update</string>
<string name="battery_optimization_notification">Battery optimizations must be turned off in
order for ReVanced Manager to work correctly in the background. Click here to turn off
optimizations.</string>
<string name="battery_optimization_notification">Battery optimizations must be turned off in order for ReVanced Manager to work correctly in the background. Click here to turn off optimizations.</string>
<string name="installing_manager_update">Installing update…</string>
<string name="downloading_manager_update">Downloading update…</string>
<string name="download_manager_failed">Failed to download update: %s</string>
@ -403,8 +361,7 @@
<string name="save_with_count">Save (%1$s)</string>
<string name="update">Update</string>
<string name="empty">Empty</string>
<string name="installing_message">Tap on <b>Update</b> when prompted. \n ReVanced Manager will
close when updating.</string>
<string name="installing_message">Tap on <b>Update</b> when prompted. \n ReVanced Manager will close when updating.</string>
<string name="no_changelogs_found">No changelogs found</string>
<string name="just_now">Just now</string>
<string name="minutes_ago">%sm ago</string>
@ -420,22 +377,18 @@
<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_description">A new version of ReVanced Manager (%s) is
available.</string>
<string name="update_available_dialog_description">A new version of ReVanced Manager (%s) is available.</string>
<string name="failed_to_download_update">Failed to download update: %s</string>
<string name="download">Download</string>
<string name="download_confirmation_metered">You are currently on a metered connection, and data
charges from your service provider may apply.\n\nDo you still want to continue?</string>
<string name="download_confirmation_metered">You are currently on a metered connection, and data charges from your service provider may apply.\n\nDo you still want to continue?</string>
<string name="download_update_confirmation">Download update?</string>
<string name="no_contributors_found">No contributors found</string>
<string name="select">Select</string>
<string name="select_deselect_all">Select or deselect all</string>
<string name="select_bundle_type_dialog_title">Add new bundle</string>
<string name="select_bundle_type_dialog_description">Add a new bundle from a URL or storage</string>
<string name="local_bundle_description">Import local files from your storage, does not
automatically update</string>
<string name="remote_bundle_description">Import remote files from a URL, can automatically
update</string>
<string name="local_bundle_description">Import local files from your storage, does not automatically update</string>
<string name="remote_bundle_description">Import remote files from a URL, can automatically update</string>
<string name="recommended">Recommended</string>
<string name="installation_failed_dialog_title">Installation failed</string>
@ -446,20 +399,13 @@
<string name="installation_invalid_dialog_title">Installation invalid</string>
<string name="installation_storage_issue_dialog_title">Not enough storage</string>
<string name="installation_timeout_dialog_title">Installation timed out</string>
<string name="installation_failed_description">The installation failed due to an unknown reason.
Try again?</string>
<string name="installation_aborted_description">The installation was cancelled manually. Try
again?</string>
<string name="installation_blocked_description">The installation was blocked. Review your device
security settings and try again.</string>
<string name="installation_conflict_description">The installation was prevented by an existing
installation of the app. Uninstall the installed app and try again?</string>
<string name="installation_incompatible_description">The app is incompatible with this device.
Use an APK that is supported by this device and try again.</string>
<string name="installation_invalid_description">The app is invalid. Uninstall the app and try
again?</string>
<string name="installation_storage_issue_description">The app could not be installed due to
insufficient storage. Free up some space and try again.</string>
<string name="installation_failed_description">The installation failed due to an unknown reason. Try again?</string>
<string name="installation_aborted_description">The installation was cancelled manually. Try again?</string>
<string name="installation_blocked_description">The installation was blocked. Review your device security settings and try again.</string>
<string name="installation_conflict_description">The installation was prevented by an existing installation of the app. Uninstall the installed app and try again?</string>
<string name="installation_incompatible_description">The app is incompatible with this device. Use an APK that is supported by this device and try again.</string>
<string name="installation_invalid_description">The app is invalid. Uninstall the app and try again?</string>
<string name="installation_storage_issue_description">The app could not be installed due to insufficient storage. Free up some space and try again.</string>
<string name="installation_timeout_description">The installation took too long. Try again?</string>
<string name="reinstall">Reinstall</string>
<string name="show">Show</string>
@ -470,14 +416,12 @@
<string name="add_patch_bundle">Add patch bundle</string>
<string name="bundle_url">Bundle URL</string>
<string name="auto_update">Auto update</string>
<string name="unsupported_patches_dialog">These patches are not compatible with the selected app
version (%1$s).\n\nClick on the patches to see more details.</string>
<string name="unsupported_patches_dialog">These patches are not compatible with the selected app version (%1$s).\n\nClick on the patches to see more details.</string>
<string name="unsupported_patch">Unsupported patch</string>
<string name="any_version">Any</string>
<string name="never_show_again">Never show again</string>
<string name="show_manager_update_dialog_on_launch">Show update message on launch</string>
<string name="show_manager_update_dialog_on_launch_description">Shows a popup notification
whenever there is a new update available on launch.</string>
<string name="show_manager_update_dialog_on_launch_description">Shows a popup notification whenever there is a new update available on launch.</string>
<string name="failed_to_import_keystore">Failed to import keystore</string>
<string name="export">Export</string>
</resources>
</resources>