feat: improve the safeguards (#2038)

This commit is contained in:
Ax333l 2024-07-06 17:43:37 +02:00 committed by GitHub
parent 3f4a234915
commit 1ee1330e47
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 136 additions and 218 deletions

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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))
}
}
}
)
}

View file

@ -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),
)
}

View file

@ -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),
)
}

View file

@ -70,8 +70,7 @@ fun AppSelectorScreen(
vm.nonSuggestedVersionDialogSubject?.let {
NonSuggestedVersionDialog(
suggestedVersion = suggestedVersions[it.packageName].orEmpty(),
onCancel = vm::dismissNonSuggestedVersionDialog,
onContinue = vm::continueWithNonSuggestedVersion,
onDismiss = vm::dismissNonSuggestedVersionDialog
)
}

View file

@ -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))
}
)
}

View file

@ -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()

View file

@ -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(

View file

@ -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)

View file

@ -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<PreferencesManager>().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() {

View file

@ -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

View file

@ -76,6 +76,10 @@
<string name="patch_compat_check_description">The check restricts patches to supported app versions</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="patch_selection_safeguard">Allow changing patch selection</string>
<string name="patch_selection_safeguard_description">Do not prevent selecting or deselecting patches</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="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>
@ -134,6 +138,7 @@
<string name="apply">Apply</string>
<string name="help">Help</string>
<string name="back">Back</string>
<string name="warning">Warning</string>
<string name="add">Add</string>
<string name="close">Close</string>
<string name="system">System</string>
@ -190,10 +195,10 @@
<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</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">You may encounter issues when not using the default patch selection and options.</string>
<string name="selection_warning_continue_countdown">Continue (%ds)</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">Supported</string>
<string name="universal">Universal</string>
<string name="unsupported">Unsupported</string>