From 1ee1330e4718f1ef26715d0461297696612c7d49 Mon Sep 17 00:00:00 2001 From: Ax333l Date: Sat, 6 Jul 2024 17:43:37 +0200 Subject: [PATCH] feat: improve the safeguards (#2038) --- .../domain/manager/PreferencesManager.kt | 5 +- .../manager/ui/component/Countdown.kt | 26 ------ .../ui/component/DangerousActionDialogBase.kt | 91 ------------------- .../ui/component/NonSuggestedVersionDialog.kt | 23 ----- .../manager/ui/component/SafeguardDialog.kt | 51 +++++++++++ .../manager/ui/screen/AppSelectorScreen.kt | 3 +- .../ui/screen/PatchesSelectorScreen.kt | 87 ++++++++++-------- .../ui/screen/VersionSelectorScreen.kt | 3 +- .../screen/settings/AdvancedSettingsScreen.kt | 12 +++ .../ui/viewmodel/AppSelectorViewModel.kt | 12 +-- .../ui/viewmodel/PatchesSelectorViewModel.kt | 24 +++-- .../ui/viewmodel/VersionSelectorViewModel.kt | 6 -- app/src/main/res/values/strings.xml | 11 ++- 13 files changed, 136 insertions(+), 218 deletions(-) delete mode 100644 app/src/main/java/app/revanced/manager/ui/component/Countdown.kt delete mode 100644 app/src/main/java/app/revanced/manager/ui/component/DangerousActionDialogBase.kt delete mode 100644 app/src/main/java/app/revanced/manager/ui/component/NonSuggestedVersionDialog.kt create mode 100644 app/src/main/java/app/revanced/manager/ui/component/SafeguardDialog.kt diff --git a/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt b/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt index dc6d713c..08b6a94d 100644 --- a/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt +++ b/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt @@ -15,7 +15,6 @@ class PreferencesManager( val multithreadingDexFileWriter = booleanPreference("multithreading_dex_file_writer", true) val useProcessRuntime = booleanPreference("use_process_runtime", false) val patcherProcessMemoryLimit = intPreference("process_runtime_memory_limit", 700) - val disablePatchVersionCompatCheck = booleanPreference("disable_patch_version_compatibility_check", false) val keystoreCommonName = stringPreference("keystore_cn", KeystoreManager.DEFAULT) val keystorePass = stringPreference("keystore_pass", KeystoreManager.DEFAULT) @@ -25,8 +24,8 @@ class PreferencesManager( val firstLaunch = booleanPreference("first_launch", true) val managerAutoUpdates = booleanPreference("manager_auto_updates", false) + val disablePatchVersionCompatCheck = booleanPreference("disable_patch_version_compatibility_check", false) val disableSelectionWarning = booleanPreference("disable_selection_warning", false) - val enableSelectionWarningCountdown = booleanPreference("enable_selection_warning_countdown", true) - + val disableUniversalPatchWarning = booleanPreference("disable_universal_patch_warning", false) val suggestedVersionSafeguard = booleanPreference("suggested_version_safeguard", true) } diff --git a/app/src/main/java/app/revanced/manager/ui/component/Countdown.kt b/app/src/main/java/app/revanced/manager/ui/component/Countdown.kt deleted file mode 100644 index 0fb23c27..00000000 --- a/app/src/main/java/app/revanced/manager/ui/component/Countdown.kt +++ /dev/null @@ -1,26 +0,0 @@ -package app.revanced.manager.ui.component - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import kotlinx.coroutines.delay - -@Composable -fun Countdown(start: Int, content: @Composable (Int) -> Unit) { - var timer by rememberSaveable(start) { - mutableStateOf(start) - } - LaunchedEffect(timer) { - if (timer == 0) { - return@LaunchedEffect - } - - delay(1000L) - timer -= 1 - } - - content(timer) -} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/component/DangerousActionDialogBase.kt b/app/src/main/java/app/revanced/manager/ui/component/DangerousActionDialogBase.kt deleted file mode 100644 index ca11b31d..00000000 --- a/app/src/main/java/app/revanced/manager/ui/component/DangerousActionDialogBase.kt +++ /dev/null @@ -1,91 +0,0 @@ -package app.revanced.manager.ui.component - -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.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.WarningAmber -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Checkbox -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.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.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import app.revanced.manager.R - -@Composable -fun DangerousActionDialogBase( - onCancel: () -> Unit, - confirmButton: @Composable (Boolean) -> Unit, - @StringRes title: Int, - body: String, -) { - var dismissPermanently by rememberSaveable { - mutableStateOf(false) - } - - AlertDialog( - onDismissRequest = onCancel, - confirmButton = { - confirmButton(dismissPermanently) - }, - dismissButton = { - TextButton(onClick = onCancel) { - Text(stringResource(R.string.cancel)) - } - }, - icon = { - Icon(Icons.Outlined.WarningAmber, null) - }, - title = { - Text( - text = stringResource(title), - style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center), - color = MaterialTheme.colorScheme.onSurface, - ) - }, - text = { - Column( - verticalArrangement = Arrangement.spacedBy(16.dp), - horizontalAlignment = Alignment.Start - ) { - Text( - text = body, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(0.dp), - modifier = Modifier - .fillMaxWidth() - .clickable { - dismissPermanently = !dismissPermanently - } - ) { - Checkbox( - checked = dismissPermanently, - onCheckedChange = { - dismissPermanently = it - } - ) - Text(stringResource(R.string.permanent_dismiss)) - } - } - } - ) -} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/component/NonSuggestedVersionDialog.kt b/app/src/main/java/app/revanced/manager/ui/component/NonSuggestedVersionDialog.kt deleted file mode 100644 index b55dd5f5..00000000 --- a/app/src/main/java/app/revanced/manager/ui/component/NonSuggestedVersionDialog.kt +++ /dev/null @@ -1,23 +0,0 @@ -package app.revanced.manager.ui.component - -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource -import app.revanced.manager.R - -@Composable -fun NonSuggestedVersionDialog(suggestedVersion: String, onCancel: () -> Unit, onContinue: (Boolean) -> Unit) { - DangerousActionDialogBase( - onCancel = onCancel, - confirmButton = { dismissPermanently -> - TextButton( - onClick = { onContinue(dismissPermanently) } - ) { - Text(stringResource(R.string.continue_)) - } - }, - title = R.string.non_suggested_version_warning_title, - body = stringResource(R.string.non_suggested_version_warning_description, suggestedVersion), - ) -} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/component/SafeguardDialog.kt b/app/src/main/java/app/revanced/manager/ui/component/SafeguardDialog.kt new file mode 100644 index 00000000..6aabe12a --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/component/SafeguardDialog.kt @@ -0,0 +1,51 @@ +package app.revanced.manager.ui.component + +import androidx.annotation.StringRes +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 SafeguardDialog( + onDismiss: () -> Unit, + @StringRes title: Int, + body: String, +) { + AlertDialog( + onDismissRequest = onDismiss, + confirmButton = { + TextButton(onClick = onDismiss) { + Text(stringResource(R.string.ok)) + } + }, + icon = { + Icon(Icons.Outlined.WarningAmber, null) + }, + title = { + Text( + text = stringResource(title), + style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center) + ) + }, + text = { + Text(body) + } + ) +} + +@Composable +fun NonSuggestedVersionDialog(suggestedVersion: String, onDismiss: () -> Unit) { + SafeguardDialog( + onDismiss = onDismiss, + title = R.string.non_suggested_version_warning_title, + body = stringResource(R.string.non_suggested_version_warning_description, suggestedVersion), + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/screen/AppSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/AppSelectorScreen.kt index c8011744..6d2e1d5f 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/AppSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/AppSelectorScreen.kt @@ -70,8 +70,7 @@ fun AppSelectorScreen( vm.nonSuggestedVersionDialogSubject?.let { NonSuggestedVersionDialog( suggestedVersion = suggestedVersions[it.packageName].orEmpty(), - onCancel = vm::dismissNonSuggestedVersionDialog, - onContinue = vm::continueWithNonSuggestedVersion, + onDismiss = vm::dismissNonSuggestedVersionDialog ) } diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt index 12d02425..e6a32f25 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt @@ -20,6 +20,7 @@ import androidx.compose.material.icons.outlined.Restore import androidx.compose.material.icons.outlined.Save import androidx.compose.material.icons.outlined.Search import androidx.compose.material.icons.outlined.Settings +import androidx.compose.material.icons.outlined.WarningAmber import androidx.compose.material3.AlertDialog import androidx.compose.material3.Checkbox import androidx.compose.material3.ExperimentalMaterial3Api @@ -37,7 +38,6 @@ 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 import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -48,17 +48,16 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import androidx.lifecycle.compose.collectAsStateWithLifecycle import app.revanced.manager.R -import app.revanced.manager.domain.manager.PreferencesManager import app.revanced.manager.patcher.patch.Option import app.revanced.manager.patcher.patch.PatchInfo import app.revanced.manager.ui.component.AppTopBar -import app.revanced.manager.ui.component.Countdown -import app.revanced.manager.ui.component.DangerousActionDialogBase +import app.revanced.manager.ui.component.SafeguardDialog import app.revanced.manager.ui.component.LazyColumnWithScrollbar import app.revanced.manager.ui.component.SearchView import app.revanced.manager.ui.component.patches.OptionItem @@ -70,7 +69,6 @@ import app.revanced.manager.util.Options import app.revanced.manager.util.PatchSelection import app.revanced.manager.util.isScrollingUp import kotlinx.coroutines.launch -import org.koin.compose.koinInject @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable @@ -160,10 +158,16 @@ fun PatchesSelectorScreen( ) } - vm.pendingSelectionAction?.let { - SelectionWarningDialog( - onCancel = vm::dismissSelectionWarning, - onConfirm = vm::confirmSelectionWarning + var showSelectionWarning by rememberSaveable { + mutableStateOf(false) + } + if (showSelectionWarning) { + SelectionWarningDialog(onDismiss = { showSelectionWarning = false }) + } + vm.pendingUniversalPatchAction?.let { + UniversalPatchWarningDialog( + onCancel = vm::dismissUniversalPatchWarning, + onConfirm = vm::confirmUniversalPatchWarning ) } @@ -196,9 +200,9 @@ fun PatchesSelectorScreen( ), onToggle = { if (vm.selectionWarningEnabled) { - vm.pendingSelectionAction = { - vm.togglePatch(uid, patch) - } + showSelectionWarning = true + } else if (vm.universalPatchWarningEnabled && patch.compatiblePackages == null) { + vm.pendingUniversalPatchAction = { vm.togglePatch(uid, patch) } } else { vm.togglePatch(uid, patch) } @@ -369,36 +373,43 @@ fun PatchesSelectorScreen( } @Composable -fun SelectionWarningDialog( +fun SelectionWarningDialog(onDismiss: () -> Unit) { + SafeguardDialog( + onDismiss = onDismiss, + title = R.string.warning, + body = stringResource(R.string.selection_warning_description), + ) +} + +@Composable +fun UniversalPatchWarningDialog( onCancel: () -> Unit, - onConfirm: (Boolean) -> Unit + onConfirm: () -> Unit ) { - val prefs: PreferencesManager = koinInject() - - DangerousActionDialogBase( - onCancel = onCancel, - confirmButton = { dismissPermanently -> - val enableCountdown by prefs.enableSelectionWarningCountdown.getAsState() - - Countdown(start = if (enableCountdown) 3 else 0) { timer -> - LaunchedEffect(timer) { - if (timer == 0) prefs.enableSelectionWarningCountdown.update(false) - } - - TextButton( - onClick = { onConfirm(dismissPermanently) }, - enabled = timer == 0 - ) { - val text = - if (timer == 0) stringResource(R.string.continue_) else stringResource( - R.string.selection_warning_continue_countdown, timer - ) - Text(text, color = MaterialTheme.colorScheme.error) - } + AlertDialog( + onDismissRequest = onCancel, + confirmButton = { + TextButton(onClick = onConfirm) { + Text(stringResource(R.string.continue_)) } }, - title = R.string.selection_warning_title, - body = stringResource(R.string.selection_warning_description), + 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)) + } ) } diff --git a/app/src/main/java/app/revanced/manager/ui/screen/VersionSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/VersionSelectorScreen.kt index 388b5dee..69548d3c 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/VersionSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/VersionSelectorScreen.kt @@ -69,8 +69,7 @@ fun VersionSelectorScreen( if (viewModel.showNonSuggestedVersionDialog) NonSuggestedVersionDialog( suggestedVersion = viewModel.requiredVersion.orEmpty(), - onCancel = viewModel::dismissNonSuggestedVersionDialog, - onContinue = viewModel::continueWithNonSuggestedVersion, + onDismiss = viewModel::dismissNonSuggestedVersionDialog ) val lazyListState = rememberLazyListState() diff --git a/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt index 146ff325..e596ea3e 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt @@ -126,12 +126,24 @@ fun AdvancedSettingsScreen( headline = R.string.patch_compat_check, description = R.string.patch_compat_check_description ) + BooleanItem( + preference = vm.prefs.disableUniversalPatchWarning, + coroutineScope = vm.viewModelScope, + headline = R.string.universal_patches_safeguard, + description = R.string.universal_patches_safeguard_description + ) BooleanItem( preference = vm.prefs.suggestedVersionSafeguard, coroutineScope = vm.viewModelScope, headline = R.string.suggested_version_safeguard, description = R.string.suggested_version_safeguard_description ) + BooleanItem( + preference = vm.prefs.disableSelectionWarning, + coroutineScope = vm.viewModelScope, + headline = R.string.patch_selection_safeguard, + description = R.string.patch_selection_safeguard_description + ) GroupHeader(stringResource(R.string.device)) SettingsListItem( diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/AppSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/AppSelectorViewModel.kt index 24a63960..a2174852 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/AppSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/AppSelectorViewModel.kt @@ -3,14 +3,12 @@ package app.revanced.manager.ui.viewmodel import android.app.Application import android.content.pm.PackageInfo import android.net.Uri -import androidx.compose.runtime.derivedStateOf 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.domain.manager.PreferencesManager import app.revanced.manager.domain.repository.PatchBundleRepository import app.revanced.manager.ui.model.SelectedApp import app.revanced.manager.util.PM @@ -25,8 +23,7 @@ import java.nio.file.Files class AppSelectorViewModel( private val app: Application, private val pm: PM, - private val patchBundleRepository: PatchBundleRepository, - private val prefs: PreferencesManager, + private val patchBundleRepository: PatchBundleRepository ) : ViewModel() { private val inputFile = File(app.cacheDir, "input.apk").also { it.delete() @@ -46,13 +43,6 @@ class AppSelectorViewModel( nonSuggestedVersionDialogSubject = null } - fun continueWithNonSuggestedVersion(dismissPermanently: Boolean) = viewModelScope.launch { - if (dismissPermanently) prefs.suggestedVersionSafeguard.update(false) - - nonSuggestedVersionDialogSubject?.let(onStorageClick) - dismissNonSuggestedVersionDialog() - } - fun handleStorageResult(uri: Uri) = viewModelScope.launch { val selectedApp = withContext(Dispatchers.IO) { loadSelectedFile(uri) diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt index 47fbc556..331548d6 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt @@ -47,10 +47,12 @@ class PatchesSelectorViewModel(input: Params) : ViewModel(), KoinComponent { private val packageName = input.app.packageName val appVersion = input.app.version - var pendingSelectionAction by mutableStateOf<(() -> Unit)?>(null) + var pendingUniversalPatchAction by mutableStateOf<(() -> Unit)?>(null) var selectionWarningEnabled by mutableStateOf(true) private set + var universalPatchWarningEnabled by mutableStateOf(true) + private set val allowIncompatiblePatches = get().disablePatchVersionCompatCheck.getBlocking() @@ -59,6 +61,8 @@ class PatchesSelectorViewModel(input: Params) : ViewModel(), KoinComponent { init { viewModelScope.launch { + universalPatchWarningEnabled = !prefs.disableUniversalPatchWarning.get() + if (prefs.disableSelectionWarning.get()) { selectionWarningEnabled = false return@launch @@ -131,21 +135,15 @@ class PatchesSelectorViewModel(input: Params) : ViewModel(), KoinComponent { customPatchSelection = selection.put(bundle, newPatches) } - fun confirmSelectionWarning(dismissPermanently: Boolean) { - selectionWarningEnabled = false + fun confirmUniversalPatchWarning() { + universalPatchWarningEnabled = false - pendingSelectionAction?.invoke() - pendingSelectionAction = null - - if (!dismissPermanently) return - - viewModelScope.launch { - prefs.disableSelectionWarning.update(true) - } + pendingUniversalPatchAction?.invoke() + pendingUniversalPatchAction = null } - fun dismissSelectionWarning() { - pendingSelectionAction = null + fun dismissUniversalPatchWarning() { + pendingUniversalPatchAction = null } fun reset() { diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/VersionSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/VersionSelectorViewModel.kt index 306397ad..d9f73264 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/VersionSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/VersionSelectorViewModel.kt @@ -162,12 +162,6 @@ class VersionSelectorViewModel( nonSuggestedVersionDialogSubject = null } - fun continueWithNonSuggestedVersion(dismissPermanently: Boolean) = viewModelScope.launch { - if (dismissPermanently) prefs.suggestedVersionSafeguard.update(false) - selectedVersion = nonSuggestedVersionDialogSubject - dismissNonSuggestedVersionDialog() - } - fun select(app: SelectedApp) { if (requiredVersion != null && app.version != requiredVersion) { nonSuggestedVersionDialogSubject = app diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 68d3dd00..a1451563 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -76,6 +76,10 @@ The check restricts patches to supported app versions Require suggested app version Enforce selection of the suggested app version + Allow changing patch selection + Do not prevent selecting or deselecting patches + Disable universal patch warning + Disables the warning that appears when you try to select universal patches Import keystore Import a custom keystore Enter keystore credentials @@ -134,6 +138,7 @@ Apply Help Back + Warning Add Close System @@ -190,10 +195,10 @@ Patch selection and options has been reset to recommended defaults Patch options have been reset Non suggested version - The version of the app you have selected does not match the suggested version.\nPlease use the suggested version: %s + 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. Stop using defaults? - You may encounter issues when not using the default patch selection and options. - Continue (%ds) + 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. + 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. Supported Universal Unsupported