fix(Compose): Adjusted universal patches safeguard and warnings (#2550)

This commit is contained in:
Brosssh 2025-05-17 21:33:02 +02:00 committed by oSumAtrIX
parent 5153e5e0cb
commit 2b380b0d7c
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
8 changed files with 88 additions and 165 deletions

View File

@ -234,7 +234,7 @@ private fun ReVancedManager(vm: MainViewModel) {
selectedAppInfoVm.updateConfiguration(patches, options) selectedAppInfoVm.updateConfiguration(patches, options)
navController.popBackStack() navController.popBackStack()
}, },
vm = koinViewModel { parametersOf(data) } viewModel = koinViewModel { parametersOf(data) }
) )
} }

View File

@ -25,7 +25,7 @@ class PreferencesManager(
val disablePatchVersionCompatCheck = booleanPreference("disable_patch_version_compatibility_check", false) val disablePatchVersionCompatCheck = booleanPreference("disable_patch_version_compatibility_check", false)
val disableSelectionWarning = booleanPreference("disable_selection_warning", false) val disableSelectionWarning = booleanPreference("disable_selection_warning", false)
val disableUniversalPatchWarning = booleanPreference("disable_universal_patch_warning", false) val disableUniversalPatchCheck = booleanPreference("disable_patch_universal_check", false)
val suggestedVersionSafeguard = booleanPreference("suggested_version_safeguard", true) val suggestedVersionSafeguard = booleanPreference("suggested_version_safeguard", true)
val acknowledgedDownloaderPlugins = stringSetPreference("acknowledged_downloader_plugins", emptySet()) val acknowledgedDownloaderPlugins = stringSetPreference("acknowledged_downloader_plugins", emptySet())

View File

@ -1,6 +1,8 @@
package app.revanced.manager.ui.component.settings package app.revanced.manager.ui.component.settings
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -9,7 +11,9 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import app.revanced.manager.R
import app.revanced.manager.domain.manager.base.Preference import app.revanced.manager.domain.manager.base.Preference
import app.revanced.manager.ui.component.ConfirmDialog
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -28,13 +32,15 @@ fun SafeguardBooleanItem(
} }
if (showSafeguardWarning) { if (showSafeguardWarning) {
SafeguardConfirmationDialog( ConfirmDialog(
onDismiss = { showSafeguardWarning = false }, onDismiss = { showSafeguardWarning = false },
onConfirm = { onConfirm = {
coroutineScope.launch { preference.update(!value) } coroutineScope.launch { preference.update(!value) }
showSafeguardWarning = false showSafeguardWarning = false
}, },
body = stringResource(confirmationText) title = stringResource(id = R.string.warning),
description = stringResource(confirmationText),
icon = Icons.Outlined.WarningAmber
) )
} }

View File

@ -1,46 +0,0 @@
package app.revanced.manager.ui.component.settings
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import app.revanced.manager.R
@Composable
fun SafeguardConfirmationDialog(
onDismiss: () -> Unit,
onConfirm: () -> Unit,
body: String,
) {
AlertDialog(
onDismissRequest = onDismiss,
confirmButton = {
TextButton(onClick = onConfirm) {
Text(stringResource(R.string.yes))
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text(stringResource(R.string.no))
}
},
icon = {
Icon(Icons.Outlined.WarningAmber, null)
},
title = {
Text(
text = stringResource(id = R.string.warning),
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center)
)
},
text = {
Text(body)
}
)
}

View File

@ -58,7 +58,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
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 androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import app.revanced.manager.R import app.revanced.manager.R
@ -88,9 +87,9 @@ import kotlinx.coroutines.launch
fun PatchesSelectorScreen( fun PatchesSelectorScreen(
onSave: (PatchSelection?, Options) -> Unit, onSave: (PatchSelection?, Options) -> Unit,
onBackClick: () -> Unit, onBackClick: () -> Unit,
vm: PatchesSelectorViewModel viewModel: PatchesSelectorViewModel
) { ) {
val bundles by vm.bundlesFlow.collectAsStateWithLifecycle(initialValue = emptyList()) val bundles by viewModel.bundlesFlow.collectAsStateWithLifecycle(initialValue = emptyList())
val pagerState = rememberPagerState( val pagerState = rememberPagerState(
initialPage = 0, initialPage = 0,
initialPageOffsetFraction = 0f initialPageOffsetFraction = 0f
@ -106,15 +105,15 @@ fun PatchesSelectorScreen(
} }
var showBottomSheet by rememberSaveable { mutableStateOf(false) } var showBottomSheet by rememberSaveable { mutableStateOf(false) }
val showSaveButton by remember { val showSaveButton by remember {
derivedStateOf { vm.selectionIsValid(bundles) } derivedStateOf { viewModel.selectionIsValid(bundles) }
} }
val defaultPatchSelectionCount by vm.defaultSelectionCount val defaultPatchSelectionCount by viewModel.defaultSelectionCount
.collectAsStateWithLifecycle(initialValue = 0) .collectAsStateWithLifecycle(initialValue = 0)
val selectedPatchCount by remember { val selectedPatchCount by remember {
derivedStateOf { derivedStateOf {
vm.customPatchSelection?.values?.sumOf { it.size } ?: defaultPatchSelectionCount viewModel.customPatchSelection?.values?.sumOf { it.size } ?: defaultPatchSelectionCount
} }
} }
@ -146,14 +145,14 @@ fun PatchesSelectorScreen(
verticalArrangement = Arrangement.spacedBy(12.dp) verticalArrangement = Arrangement.spacedBy(12.dp)
) { ) {
CheckedFilterChip( CheckedFilterChip(
selected = vm.filter and SHOW_INCOMPATIBLE == 0, selected = viewModel.filter and SHOW_INCOMPATIBLE == 0,
onClick = { vm.toggleFlag(SHOW_INCOMPATIBLE) }, onClick = { viewModel.toggleFlag(SHOW_INCOMPATIBLE) },
label = { Text(stringResource(R.string.this_version)) } label = { Text(stringResource(R.string.this_version)) }
) )
CheckedFilterChip( CheckedFilterChip(
selected = vm.filter and SHOW_UNIVERSAL != 0, selected = viewModel.filter and SHOW_UNIVERSAL != 0,
onClick = { vm.toggleFlag(SHOW_UNIVERSAL) }, onClick = { viewModel.toggleFlag(SHOW_UNIVERSAL) },
label = { Text(stringResource(R.string.universal)) }, label = { Text(stringResource(R.string.universal)) },
) )
} }
@ -161,43 +160,39 @@ fun PatchesSelectorScreen(
} }
} }
if (vm.compatibleVersions.isNotEmpty()) if (viewModel.compatibleVersions.isNotEmpty())
IncompatiblePatchDialog( IncompatiblePatchDialog(
appVersion = vm.appVersion ?: stringResource(R.string.any_version), appVersion = viewModel.appVersion ?: stringResource(R.string.any_version),
compatibleVersions = vm.compatibleVersions, compatibleVersions = viewModel.compatibleVersions,
onDismissRequest = vm::dismissDialogs onDismissRequest = viewModel::dismissDialogs
) )
var showIncompatiblePatchesDialog by rememberSaveable { var showIncompatiblePatchesDialog by rememberSaveable {
mutableStateOf(false) mutableStateOf(false)
} }
if (showIncompatiblePatchesDialog) if (showIncompatiblePatchesDialog)
IncompatiblePatchesDialog( IncompatiblePatchesDialog(
appVersion = vm.appVersion ?: stringResource(R.string.any_version), appVersion = viewModel.appVersion ?: stringResource(R.string.any_version),
onDismissRequest = { showIncompatiblePatchesDialog = false } onDismissRequest = { showIncompatiblePatchesDialog = false }
) )
vm.optionsDialog?.let { (bundle, patch) -> viewModel.optionsDialog?.let { (bundle, patch) ->
OptionsDialog( OptionsDialog(
onDismissRequest = vm::dismissDialogs, onDismissRequest = viewModel::dismissDialogs,
patch = patch, patch = patch,
values = vm.getOptions(bundle, patch), values = viewModel.getOptions(bundle, patch),
reset = { vm.resetOptions(bundle, patch) }, reset = { viewModel.resetOptions(bundle, patch) },
set = { key, value -> vm.setOption(bundle, patch, key, value) } set = { key, value -> viewModel.setOption(bundle, patch, key, value) }
) )
} }
var showSelectionWarning by rememberSaveable { var showSelectionWarning by rememberSaveable { mutableStateOf(false) }
mutableStateOf(false) var showUniversalWarning by rememberSaveable { mutableStateOf(false) }
}
if (showSelectionWarning) { if (showSelectionWarning)
SelectionWarningDialog(onDismiss = { showSelectionWarning = false }) SelectionWarningDialog(onDismiss = { showSelectionWarning = false })
}
vm.pendingUniversalPatchAction?.let { if (showUniversalWarning)
UniversalPatchWarningDialog( UniversalPatchWarningDialog(onDismiss = { showUniversalWarning = false })
onCancel = vm::dismissUniversalPatchWarning,
onConfirm = vm::confirmUniversalPatchWarning
)
}
fun LazyListScope.patchList( fun LazyListScope.patchList(
uid: Int, uid: Int,
@ -221,27 +216,25 @@ fun PatchesSelectorScreen(
PatchItem( PatchItem(
patch = patch, patch = patch,
onOptionsDialog = { onOptionsDialog = {
vm.optionsDialog = uid to patch viewModel.optionsDialog = uid to patch
}, },
selected = compatible && vm.isSelected( selected = compatible && viewModel.isSelected(
uid, uid,
patch patch
), ),
onToggle = { onToggle = {
when { when {
// Open incompatible dialog if the patch is not supported // Open incompatible dialog if the patch is not supported
!compatible -> vm.openIncompatibleDialog(patch) !compatible -> viewModel.openIncompatibleDialog(patch)
// Show selection warning if enabled // Show selection warning if enabled
vm.selectionWarningEnabled -> showSelectionWarning = true viewModel.selectionWarningEnabled -> showSelectionWarning = true
// Set pending universal patch action if the universal patch warning is enabled and there are no compatible packages // Show universal warning if enabled
vm.universalPatchWarningEnabled && patch.compatiblePackages == null -> { viewModel.universalPatchWarningEnabled -> showUniversalWarning = true
vm.pendingUniversalPatchAction = { vm.togglePatch(uid, patch) }
}
// Toggle the patch otherwise // Toggle the patch otherwise
else -> vm.togglePatch(uid, patch) else -> viewModel.togglePatch(uid, patch)
} }
}, },
compatible = compatible compatible = compatible
@ -327,7 +320,7 @@ fun PatchesSelectorScreen(
patchList( patchList(
uid = bundle.uid, uid = bundle.uid,
patches = bundle.universal.searched(), patches = bundle.universal.searched(),
visible = vm.filter and SHOW_UNIVERSAL != 0, visible = viewModel.filter and SHOW_UNIVERSAL != 0,
compatible = true compatible = true
) { ) {
ListHeader( ListHeader(
@ -338,8 +331,8 @@ fun PatchesSelectorScreen(
patchList( patchList(
uid = bundle.uid, uid = bundle.uid,
patches = bundle.incompatible.searched(), patches = bundle.incompatible.searched(),
visible = vm.filter and SHOW_INCOMPATIBLE != 0, visible = viewModel.filter and SHOW_INCOMPATIBLE != 0,
compatible = vm.allowIncompatiblePatches compatible = viewModel.allowIncompatiblePatches
) { ) {
ListHeader( ListHeader(
title = stringResource(R.string.incompatible_patches), title = stringResource(R.string.incompatible_patches),
@ -362,7 +355,7 @@ fun PatchesSelectorScreen(
verticalArrangement = Arrangement.spacedBy(4.dp) verticalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
SmallFloatingActionButton( SmallFloatingActionButton(
onClick = vm::reset, onClick = viewModel::reset,
containerColor = MaterialTheme.colorScheme.tertiaryContainer containerColor = MaterialTheme.colorScheme.tertiaryContainer
) { ) {
Icon(Icons.Outlined.Restore, stringResource(R.string.reset)) Icon(Icons.Outlined.Restore, stringResource(R.string.reset))
@ -385,7 +378,7 @@ fun PatchesSelectorScreen(
expanded = patchLazyListStates.getOrNull(pagerState.currentPage)?.isScrollingUp expanded = patchLazyListStates.getOrNull(pagerState.currentPage)?.isScrollingUp
?: true, ?: true,
onClick = { onClick = {
onSave(vm.getCustomSelection(), vm.getOptions()) onSave(viewModel.getCustomSelection(), viewModel.getOptions())
} }
) )
} }
@ -453,7 +446,7 @@ fun PatchesSelectorScreen(
patchList( patchList(
uid = bundle.uid, uid = bundle.uid,
patches = bundle.universal, patches = bundle.universal,
visible = vm.filter and SHOW_UNIVERSAL != 0, visible = viewModel.filter and SHOW_UNIVERSAL != 0,
compatible = true compatible = true
) { ) {
ListHeader( ListHeader(
@ -463,8 +456,8 @@ fun PatchesSelectorScreen(
patchList( patchList(
uid = bundle.uid, uid = bundle.uid,
patches = bundle.incompatible, patches = bundle.incompatible,
visible = vm.filter and SHOW_INCOMPATIBLE != 0, visible = viewModel.filter and SHOW_INCOMPATIBLE != 0,
compatible = vm.allowIncompatiblePatches compatible = viewModel.allowIncompatiblePatches
) { ) {
ListHeader( ListHeader(
title = stringResource(R.string.incompatible_patches), title = stringResource(R.string.incompatible_patches),
@ -479,7 +472,9 @@ fun PatchesSelectorScreen(
} }
@Composable @Composable
private fun SelectionWarningDialog(onDismiss: () -> Unit) { private fun SelectionWarningDialog(
onDismiss: () -> Unit
) {
SafeguardDialog( SafeguardDialog(
onDismiss = onDismiss, onDismiss = onDismiss,
title = R.string.warning, title = R.string.warning,
@ -489,33 +484,12 @@ private fun SelectionWarningDialog(onDismiss: () -> Unit) {
@Composable @Composable
private fun UniversalPatchWarningDialog( private fun UniversalPatchWarningDialog(
onCancel: () -> Unit, onDismiss: () -> Unit
onConfirm: () -> Unit
) { ) {
AlertDialog( SafeguardDialog(
onDismissRequest = onCancel, onDismiss = onDismiss,
confirmButton = { title = R.string.warning,
TextButton(onClick = onConfirm) { body = stringResource(R.string.universal_patch_warning_description),
Text(stringResource(R.string.continue_))
}
},
dismissButton = {
TextButton(onClick = onCancel) {
Text(stringResource(R.string.cancel))
}
},
icon = {
Icon(Icons.Outlined.WarningAmber, null)
},
title = {
Text(
text = stringResource(R.string.warning),
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center)
)
},
text = {
Text(stringResource(R.string.universal_patch_warning_description))
}
) )
} }

View File

@ -61,7 +61,7 @@ import org.koin.androidx.compose.koinViewModel
@Composable @Composable
fun AdvancedSettingsScreen( fun AdvancedSettingsScreen(
onBackClick: () -> Unit, onBackClick: () -> Unit,
vm: AdvancedSettingsViewModel = koinViewModel() viewModel: AdvancedSettingsViewModel = koinViewModel()
) { ) {
val context = LocalContext.current val context = LocalContext.current
val memoryLimit = remember { val memoryLimit = remember {
@ -91,16 +91,16 @@ fun AdvancedSettingsScreen(
) { ) {
GroupHeader(stringResource(R.string.manager)) GroupHeader(stringResource(R.string.manager))
val apiUrl by vm.prefs.api.getAsState() val apiUrl by viewModel.prefs.api.getAsState()
var showApiUrlDialog by rememberSaveable { mutableStateOf(false) } var showApiUrlDialog by rememberSaveable { mutableStateOf(false) }
if (showApiUrlDialog) { if (showApiUrlDialog) {
APIUrlDialog( APIUrlDialog(
currentUrl = apiUrl, currentUrl = apiUrl,
defaultUrl = vm.prefs.api.default, defaultUrl = viewModel.prefs.api.default,
onSubmit = { onSubmit = {
showApiUrlDialog = false showApiUrlDialog = false
it?.let(vm::setApiUrl) it?.let(viewModel::setApiUrl)
} }
) )
} }
@ -114,44 +114,44 @@ fun AdvancedSettingsScreen(
GroupHeader(stringResource(R.string.safeguards)) GroupHeader(stringResource(R.string.safeguards))
SafeguardBooleanItem( SafeguardBooleanItem(
preference = vm.prefs.disablePatchVersionCompatCheck, preference = viewModel.prefs.disablePatchVersionCompatCheck,
coroutineScope = vm.viewModelScope, coroutineScope = viewModel.viewModelScope,
headline = R.string.patch_compat_check, headline = R.string.patch_compat_check,
description = R.string.patch_compat_check_description, description = R.string.patch_compat_check_description,
confirmationText = R.string.patch_compat_check_confirmation confirmationText = R.string.patch_compat_check_confirmation
) )
SafeguardBooleanItem( SafeguardBooleanItem(
preference = vm.prefs.disableUniversalPatchWarning, preference = viewModel.prefs.suggestedVersionSafeguard,
coroutineScope = vm.viewModelScope, coroutineScope = viewModel.viewModelScope,
headline = R.string.universal_patches_safeguard,
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, headline = R.string.suggested_version_safeguard,
description = R.string.suggested_version_safeguard_description, description = R.string.suggested_version_safeguard_description,
confirmationText = R.string.suggested_version_safeguard_confirmation confirmationText = R.string.suggested_version_safeguard_confirmation
) )
SafeguardBooleanItem( SafeguardBooleanItem(
preference = vm.prefs.disableSelectionWarning, preference = viewModel.prefs.disableSelectionWarning,
coroutineScope = vm.viewModelScope, coroutineScope = viewModel.viewModelScope,
headline = R.string.patch_selection_safeguard, headline = R.string.patch_selection_safeguard,
description = R.string.patch_selection_safeguard_description, description = R.string.patch_selection_safeguard_description,
confirmationText = R.string.patch_selection_safeguard_confirmation confirmationText = R.string.patch_selection_safeguard_confirmation
) )
SafeguardBooleanItem(
preference = viewModel.prefs.disableUniversalPatchCheck,
coroutineScope = viewModel.viewModelScope,
headline = R.string.universal_patches_safeguard,
description = R.string.universal_patches_safeguard_description,
confirmationText = R.string.universal_patches_safeguard_confirmation
)
GroupHeader(stringResource(R.string.patcher)) GroupHeader(stringResource(R.string.patcher))
BooleanItem( BooleanItem(
preference = vm.prefs.useProcessRuntime, preference = viewModel.prefs.useProcessRuntime,
coroutineScope = vm.viewModelScope, coroutineScope = viewModel.viewModelScope,
headline = R.string.process_runtime, headline = R.string.process_runtime,
description = R.string.process_runtime_description, description = R.string.process_runtime_description,
) )
IntegerItem( IntegerItem(
preference = vm.prefs.patcherProcessMemoryLimit, preference = viewModel.prefs.patcherProcessMemoryLimit,
coroutineScope = vm.viewModelScope, coroutineScope = viewModel.viewModelScope,
headline = R.string.process_runtime_memory_limit, headline = R.string.process_runtime_memory_limit,
description = R.string.process_runtime_memory_limit_description, description = R.string.process_runtime_memory_limit_description,
) )
@ -159,11 +159,11 @@ fun AdvancedSettingsScreen(
GroupHeader(stringResource(R.string.debugging)) GroupHeader(stringResource(R.string.debugging))
val exportDebugLogsLauncher = val exportDebugLogsLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) {
it?.let(vm::exportDebugLogs) it?.let(viewModel::exportDebugLogs)
} }
SettingsListItem( SettingsListItem(
headlineContent = stringResource(R.string.debug_logs_export), headlineContent = stringResource(R.string.debug_logs_export),
modifier = Modifier.clickable { exportDebugLogsLauncher.launch(vm.debugLogFileName) } modifier = Modifier.clickable { exportDebugLogsLauncher.launch(viewModel.debugLogFileName) }
) )
val clipboard = remember { context.getSystemService<ClipboardManager>()!! } val clipboard = remember { context.getSystemService<ClipboardManager>()!! }
val deviceContent = """ val deviceContent = """

View File

@ -55,8 +55,6 @@ class PatchesSelectorViewModel(input: SelectedApplicationInfo.PatchesSelector.Vi
private val packageName = input.app.packageName private val packageName = input.app.packageName
val appVersion = input.app.version val appVersion = input.app.version
var pendingUniversalPatchAction by mutableStateOf<(() -> Unit)?>(null)
var selectionWarningEnabled by mutableStateOf(true) var selectionWarningEnabled by mutableStateOf(true)
private set private set
var universalPatchWarningEnabled by mutableStateOf(true) var universalPatchWarningEnabled by mutableStateOf(true)
@ -69,7 +67,9 @@ class PatchesSelectorViewModel(input: SelectedApplicationInfo.PatchesSelector.Vi
init { init {
viewModelScope.launch { viewModelScope.launch {
universalPatchWarningEnabled = !prefs.disableUniversalPatchWarning.get() if (prefs.disableUniversalPatchCheck.get()) {
universalPatchWarningEnabled = false
}
if (prefs.disableSelectionWarning.get()) { if (prefs.disableSelectionWarning.get()) {
selectionWarningEnabled = false selectionWarningEnabled = false
@ -160,17 +160,6 @@ class PatchesSelectorViewModel(input: SelectedApplicationInfo.PatchesSelector.Vi
customPatchSelection = selection.put(bundle, newPatches) customPatchSelection = selection.put(bundle, newPatches)
} }
fun confirmUniversalPatchWarning() {
universalPatchWarningEnabled = false
pendingUniversalPatchAction?.invoke()
pendingUniversalPatchAction = null
}
fun dismissUniversalPatchWarning() {
pendingUniversalPatchAction = null
}
fun reset() { fun reset() {
patchOptions.clear() patchOptions.clear()
customPatchSelection = null customPatchSelection = null

View File

@ -100,8 +100,8 @@
<string name="patch_selection_safeguard">Allow changing patch selection</string> <string name="patch_selection_safeguard">Allow changing patch selection</string>
<string name="patch_selection_safeguard_description">Do not prevent selecting or deselecting patches</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="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">Allow using universal patches</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_description">Do not prevent using 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="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">Import keystore</string>
<string name="import_keystore_description">Import a custom keystore</string> <string name="import_keystore_description">Import a custom keystore</string>
@ -226,7 +226,7 @@
<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_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="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="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\nYou need to turn on \"Allow using universal patches\" in the advanced settings before using universal patches.</string>
<string name="this_version">This version</string> <string name="this_version">This version</string>
<string name="universal">Any app</string> <string name="universal">Any app</string>
<string name="search_patches">Search patches</string> <string name="search_patches">Search patches</string>