refactor: fix terminology and wording related to patches (#1623)

This commit is contained in:
Ax333l 2024-01-18 20:50:24 +01:00 committed by GitHub
parent 36c8f59d6f
commit c0f3d02e6f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 102 additions and 106 deletions

View file

@ -107,11 +107,11 @@ class MainActivity : ComponentActivity() {
)
is Destination.InstalledApplicationInfo -> InstalledAppInfoScreen(
onPatchClick = { packageName, patchesSelection ->
onPatchClick = { packageName, patchSelection ->
navController.navigate(
Destination.VersionSelector(
packageName,
patchesSelection
patchSelection
)
)
},
@ -142,14 +142,14 @@ class MainActivity : ComponentActivity() {
navController.navigate(
Destination.SelectedApplicationInfo(
selectedApp,
destination.patchesSelection,
destination.patchSelection,
)
)
},
viewModel = getComposeViewModel {
parametersOf(
destination.packageName,
destination.patchesSelection
destination.patchSelection
)
}
)
@ -167,7 +167,7 @@ class MainActivity : ComponentActivity() {
parametersOf(
SelectedAppInfoViewModel.Params(
destination.selectedApp,
destination.patchesSelection
destination.patchSelection
)
)
}

View file

@ -4,7 +4,7 @@ import app.revanced.manager.data.room.AppDatabase
import app.revanced.manager.data.room.apps.installed.AppliedPatch
import app.revanced.manager.data.room.apps.installed.InstallType
import app.revanced.manager.data.room.apps.installed.InstalledApp
import app.revanced.manager.util.PatchesSelection
import app.revanced.manager.util.PatchSelection
import kotlinx.coroutines.flow.distinctUntilChanged
class InstalledAppRepository(
@ -16,7 +16,7 @@ class InstalledAppRepository(
suspend fun get(packageName: String) = dao.get(packageName)
suspend fun getAppliedPatches(packageName: String): PatchesSelection =
suspend fun getAppliedPatches(packageName: String): PatchSelection =
dao.getPatchesSelection(packageName).mapValues { (_, patches) -> patches.toSet() }
suspend fun addOrUpdate(
@ -24,7 +24,7 @@ class InstalledAppRepository(
originalPackageName: String,
version: String,
installType: InstallType,
patchesSelection: PatchesSelection
patchSelection: PatchSelection
) {
dao.upsertApp(
InstalledApp(
@ -33,7 +33,7 @@ class InstalledAppRepository(
version = version,
installType = installType
),
patchesSelection.flatMap { (uid, patches) ->
patchSelection.flatMap { (uid, patches) ->
patches.map { patch ->
AppliedPatch(
packageName = currentPackageName,

View file

@ -33,7 +33,7 @@ import app.revanced.manager.ui.model.SelectedApp
import app.revanced.manager.ui.model.State
import app.revanced.manager.util.Options
import app.revanced.manager.util.PM
import app.revanced.manager.util.PatchesSelection
import app.revanced.manager.util.PatchSelection
import app.revanced.manager.util.tag
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
@ -59,7 +59,7 @@ class PatcherWorker(
data class Args(
val input: SelectedApp,
val output: String,
val selectedPatches: PatchesSelection,
val selectedPatches: PatchSelection,
val options: Options,
val logger: ManagerLogger,
val downloadProgress: MutableStateFlow<Pair<Float, Float>?>,

View file

@ -89,7 +89,7 @@ fun BundleItem(
supportingContent = {
state.patchBundleOrNull()?.patches?.size?.let { patchCount ->
Text(
text = pluralStringResource(R.plurals.patches_count, patchCount, patchCount),
text = pluralStringResource(R.plurals.patch_count, patchCount, patchCount),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)

View file

@ -4,7 +4,7 @@ import android.os.Parcelable
import app.revanced.manager.data.room.apps.installed.InstalledApp
import app.revanced.manager.ui.model.SelectedApp
import app.revanced.manager.util.Options
import app.revanced.manager.util.PatchesSelection
import app.revanced.manager.util.PatchSelection
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue
@ -23,12 +23,12 @@ sealed interface Destination : Parcelable {
data class Settings(val startDestination: SettingsDestination = SettingsDestination.Settings) : Destination
@Parcelize
data class VersionSelector(val packageName: String, val patchesSelection: PatchesSelection? = null) : Destination
data class VersionSelector(val packageName: String, val patchSelection: PatchSelection? = null) : Destination
@Parcelize
data class SelectedApplicationInfo(val selectedApp: SelectedApp, val patchesSelection: PatchesSelection? = null) : Destination
data class SelectedApplicationInfo(val selectedApp: SelectedApp, val patchSelection: PatchSelection? = null) : Destination
@Parcelize
data class Patcher(val selectedApp: SelectedApp, val selectedPatches: PatchesSelection, val options: @RawValue Options) : Destination
data class Patcher(val selectedApp: SelectedApp, val selectedPatches: PatchSelection, val options: @RawValue Options) : Destination
}

View file

@ -3,7 +3,7 @@ package app.revanced.manager.ui.destination
import android.os.Parcelable
import app.revanced.manager.ui.model.SelectedApp
import app.revanced.manager.util.Options
import app.revanced.manager.util.PatchesSelection
import app.revanced.manager.util.PatchSelection
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue
@ -12,7 +12,7 @@ sealed interface SelectedAppInfoDestination : Parcelable {
data object Main : SelectedAppInfoDestination
@Parcelize
data class PatchesSelector(val app: SelectedApp, val currentSelection: PatchesSelection?, val options: @RawValue Options) : SelectedAppInfoDestination
data class PatchesSelector(val app: SelectedApp, val currentSelection: PatchSelection?, val options: @RawValue Options) : SelectedAppInfoDestination
@Parcelize
data object VersionSelector: SelectedAppInfoDestination

View file

@ -2,7 +2,7 @@ package app.revanced.manager.ui.model
import app.revanced.manager.domain.repository.PatchBundleRepository
import app.revanced.manager.patcher.patch.PatchInfo
import app.revanced.manager.util.PatchesSelection
import app.revanced.manager.util.PatchSelection
import app.revanced.manager.util.flatMapLatestAndCombine
import kotlinx.coroutines.flow.map
@ -34,7 +34,7 @@ data class BundleInfo(
}
companion object Extensions {
inline fun Iterable<BundleInfo>.toPatchSelection(allowUnsupported: Boolean, condition: (Int, PatchInfo) -> Boolean): PatchesSelection = this.associate { bundle ->
inline fun Iterable<BundleInfo>.toPatchSelection(allowUnsupported: Boolean, condition: (Int, PatchInfo) -> Boolean): PatchSelection = this.associate { bundle ->
val patches =
bundle.patchSequence(allowUnsupported)
.mapNotNullTo(mutableSetOf()) { patch ->

View file

@ -111,7 +111,7 @@ fun AppSelectorScreen(
{
Text(
pluralStringResource(
R.plurals.patches_count,
R.plurals.patch_count,
it,
it
)
@ -199,7 +199,7 @@ fun AppSelectorScreen(
{
Text(
pluralStringResource(
R.plurals.patches_count,
R.plurals.patch_count,
it,
it
)

View file

@ -29,6 +29,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@ -40,15 +41,18 @@ import app.revanced.manager.ui.component.ColumnWithScrollbar
import app.revanced.manager.ui.component.SegmentedButton
import app.revanced.manager.ui.component.settings.SettingsListItem
import app.revanced.manager.ui.viewmodel.InstalledAppInfoViewModel
import app.revanced.manager.util.PatchesSelection
import app.revanced.manager.util.PatchSelection
import app.revanced.manager.util.toast
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun InstalledAppInfoScreen(
onPatchClick: (packageName: String, patchesSelection: PatchesSelection) -> Unit,
onPatchClick: (packageName: String, patchSelection: PatchSelection) -> Unit,
onBackClick: () -> Unit,
viewModel: InstalledAppInfoViewModel
) {
val context = LocalContext.current
SideEffect {
viewModel.onBackClick = onBackClick
}
@ -142,12 +146,12 @@ fun InstalledAppInfoScreen(
modifier = Modifier.padding(vertical = 16.dp)
) {
SettingsListItem(
modifier = Modifier.clickable { },
modifier = Modifier.clickable { context.toast("Not implemented yet!") },
headlineContent = stringResource(R.string.applied_patches),
supportingContent =
(viewModel.appliedPatches?.values?.sumOf { it.size } ?: 0).let {
pluralStringResource(
id = R.plurals.applied_patches,
id = R.plurals.patch_count,
it,
it
)

View file

@ -68,14 +68,14 @@ import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel.Companion.SHOW
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel.Companion.SHOW_UNIVERSAL
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel.Companion.SHOW_UNSUPPORTED
import app.revanced.manager.util.Options
import app.revanced.manager.util.PatchesSelection
import app.revanced.manager.util.PatchSelection
import kotlinx.coroutines.launch
import org.koin.compose.rememberKoinInject
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun PatchesSelectorScreen(
onSave: (PatchesSelection?, Options) -> Unit,
onSave: (PatchSelection?, Options) -> Unit,
onBackClick: () -> Unit,
vm: PatchesSelectorViewModel
) {
@ -105,13 +105,13 @@ fun PatchesSelectorScreen(
modifier = Modifier.padding(horizontal = 24.dp)
) {
Text(
text = stringResource(R.string.patches_selector_sheet_filter_title),
text = stringResource(R.string.patch_selector_sheet_filter_title),
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.padding(bottom = 16.dp)
)
Text(
text = stringResource(R.string.patches_selector_sheet_filter_compat_title),
text = stringResource(R.string.patch_selector_sheet_filter_compat_title),
style = MaterialTheme.typography.titleMedium
)

View file

@ -32,7 +32,7 @@ import app.revanced.manager.ui.model.SelectedApp
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel
import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel
import app.revanced.manager.util.Options
import app.revanced.manager.util.PatchesSelection
import app.revanced.manager.util.PatchSelection
import app.revanced.manager.util.toast
import dev.olshevski.navigation.reimagined.AnimatedNavHost
import dev.olshevski.navigation.reimagined.NavBackHandler
@ -44,7 +44,7 @@ import org.koin.core.parameter.parametersOf
@Composable
fun SelectedAppInfoScreen(
onPatchClick: (SelectedApp, PatchesSelection, Options) -> Unit,
onPatchClick: (SelectedApp, PatchSelection, Options) -> Unit,
onBackClick: () -> Unit,
vm: SelectedAppInfoViewModel
) {

View file

@ -159,7 +159,7 @@ fun SelectedAppItem(
else -> null
},
trailingContent = patchCount?.let { {
Text(pluralStringResource(R.plurals.patches_count, it, it))
Text(pluralStringResource(R.plurals.patch_count, it, it))
} },
modifier = Modifier
.clickable(enabled = !alreadyPatched && enabled, onClick = onClick)

View file

@ -131,22 +131,22 @@ fun ImportExportSettingsScreen(
description = R.string.regenerate_keystore_description
)
GroupHeader(stringResource(R.string.patches_selection))
GroupHeader(stringResource(R.string.patches))
GroupItem(
onClick = vm::importSelection,
headline = R.string.restore_patches_selection,
description = R.string.restore_patches_selection_description
headline = R.string.import_patch_selection,
description = R.string.import_patch_selection_description
)
GroupItem(
onClick = vm::exportSelection,
headline = R.string.backup_patches_selection,
description = R.string.backup_patches_selection_description
headline = R.string.export_patch_selection,
description = R.string.export_patch_selection_description
)
// TODO: allow resetting selection for specific bundle or package name.
GroupItem(
onClick = vm::resetSelection,
headline = R.string.clear_patches_selection,
description = R.string.clear_patches_selection_description
headline = R.string.reset_patch_selection,
description = R.string.reset_patch_selection_description
)
var showPackageSelector by rememberSaveable {
@ -158,7 +158,7 @@ fun ImportExportSettingsScreen(
if (showPackageSelector)
PackageSelector(packages = packagesWithOptions) { selected ->
selected?.let(vm::clearOptionsForPackage)
selected?.let(vm::resetOptionsForPackage)
showPackageSelector = false
}
@ -170,23 +170,22 @@ fun ImportExportSettingsScreen(
showBundleSelector = false
}
GroupHeader(stringResource(R.string.patch_options))
// TODO: patch options import/export.
GroupItem(
onClick = vm::resetOptions,
headline = R.string.patch_options_reset_all,
description = R.string.patch_options_reset_all_description,
)
GroupItem(
onClick = { showPackageSelector = true },
headline = R.string.patch_options_clear_package,
description = R.string.patch_options_clear_package_description
headline = R.string.patch_options_reset_package,
description = R.string.patch_options_reset_package_description
)
if (patchBundles.size > 1)
GroupItem(
onClick = { showBundleSelector = true },
headline = R.string.patch_options_clear_bundle,
description = R.string.patch_options_clear_bundle_description,
)
GroupItem(
onClick = vm::resetOptions,
headline = R.string.patch_options_clear_all,
description = R.string.patch_options_clear_all_description,
headline = R.string.patch_options_reset_bundle,
description = R.string.patch_options_reset_bundle_description,
)
}
}

View file

@ -53,7 +53,7 @@ class ImportExportViewModel(
val packagesWithOptions = optionsRepository.getPackagesWithSavedOptions()
fun clearOptionsForPackage(packageName: String) = viewModelScope.launch {
fun resetOptionsForPackage(packageName: String) = viewModelScope.launch {
optionsRepository.clearOptionsForPackage(packageName)
}
@ -163,8 +163,8 @@ class ImportExportViewModel(
override val activityArg = JSON_MIMETYPE
override suspend fun execute(bundleUid: Int, location: Uri) = uiSafe(
app,
R.string.restore_patches_selection_fail,
"Failed to restore patches selection"
R.string.import_patch_selection_fail,
"Failed to restore patch selection"
) {
val selection = withContext(Dispatchers.IO) {
contentResolver.openInputStream(location)!!.use {
@ -181,8 +181,8 @@ class ImportExportViewModel(
override val activityArg = "selection.json"
override suspend fun execute(bundleUid: Int, location: Uri) = uiSafe(
app,
R.string.backup_patches_selection_fail,
"Failed to backup patches selection"
R.string.export_patch_selection_fail,
"Failed to backup patch selection"
) {
val selection = selectionRepository.export(bundleUid)

View file

@ -21,7 +21,7 @@ import app.revanced.manager.domain.installer.RootInstaller
import app.revanced.manager.domain.repository.InstalledAppRepository
import app.revanced.manager.service.UninstallService
import app.revanced.manager.util.PM
import app.revanced.manager.util.PatchesSelection
import app.revanced.manager.util.PatchSelection
import app.revanced.manager.util.simpleMessage
import app.revanced.manager.util.tag
import app.revanced.manager.util.toast
@ -43,7 +43,7 @@ class InstalledAppInfoViewModel(
var appInfo: PackageInfo? by mutableStateOf(null)
private set
var appliedPatches: PatchesSelection? by mutableStateOf(null)
var appliedPatches: PatchSelection? by mutableStateOf(null)
var isMounted by mutableStateOf(rootInstaller.isAppMounted(installedApp.currentPackageName))
private set

View file

@ -24,7 +24,7 @@ import app.revanced.manager.ui.model.BundleInfo.Extensions.bundleInfoFlow
import app.revanced.manager.ui.model.BundleInfo.Extensions.toPatchSelection
import app.revanced.manager.ui.model.SelectedApp
import app.revanced.manager.util.Options
import app.revanced.manager.util.PatchesSelection
import app.revanced.manager.util.PatchSelection
import app.revanced.manager.util.saver.Nullable
import app.revanced.manager.util.saver.nullableSaver
import app.revanced.manager.util.saver.persistentMapSaver
@ -36,7 +36,6 @@ import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
import kotlinx.collections.immutable.*
import java.util.Optional
@Stable
@OptIn(SavedStateHandleSaveableApi::class)
@ -72,11 +71,11 @@ class PatchesSelectorViewModel(input: Params) : ViewModel(), KoinComponent {
}
private var hasModifiedSelection = false
private var customPatchesSelection: PersistentPatchesSelection? by savedStateHandle.saveable(
private var customPatchSelection: PersistentPatchSelection? by savedStateHandle.saveable(
key = "selection",
stateSaver = patchesSaver,
stateSaver = selectionSaver,
) {
mutableStateOf(input.currentSelection?.toPersistentPatchesSelection())
mutableStateOf(input.currentSelection?.toPersistentPatchSelection())
}
private val patchOptions: PersistentOptions by savedStateHandle.saveable(
@ -98,12 +97,12 @@ class PatchesSelectorViewModel(input: Params) : ViewModel(), KoinComponent {
var filter by mutableIntStateOf(SHOW_SUPPORTED or SHOW_UNIVERSAL or SHOW_UNSUPPORTED)
private set
private suspend fun generateDefaultSelection(): PersistentPatchesSelection {
private suspend fun generateDefaultSelection(): PersistentPatchSelection {
val bundles = bundlesFlow.first()
val generatedSelection =
bundles.toPatchSelection(allowExperimental) { _, patch -> patch.include }
return generatedSelection.toPersistentPatchesSelection()
return generatedSelection.toPersistentPatchSelection()
}
fun selectionIsValid(bundles: List<BundleInfo>) = bundles.any { bundle ->
@ -112,14 +111,14 @@ class PatchesSelectorViewModel(input: Params) : ViewModel(), KoinComponent {
}
}
fun isSelected(bundle: Int, patch: PatchInfo) = customPatchesSelection?.let { selection ->
fun isSelected(bundle: Int, patch: PatchInfo) = customPatchSelection?.let { selection ->
selection[bundle]?.contains(patch.name) ?: false
} ?: patch.include
fun togglePatch(bundle: Int, patch: PatchInfo) = viewModelScope.launch {
hasModifiedSelection = true
val selection = customPatchesSelection ?: generateDefaultSelection()
val selection = customPatchSelection ?: generateDefaultSelection()
val newPatches = selection[bundle]?.let { patches ->
if (patch.name in patches)
patches.remove(patch.name)
@ -127,7 +126,7 @@ class PatchesSelectorViewModel(input: Params) : ViewModel(), KoinComponent {
patches.add(patch.name)
} ?: persistentSetOf(patch.name)
customPatchesSelection = selection.put(bundle, newPatches)
customPatchSelection = selection.put(bundle, newPatches)
}
fun confirmSelectionWarning(dismissPermanently: Boolean) {
@ -149,15 +148,15 @@ class PatchesSelectorViewModel(input: Params) : ViewModel(), KoinComponent {
fun reset() {
patchOptions.clear()
customPatchesSelection = null
customPatchSelection = null
hasModifiedSelection = false
app.toast(app.getString(R.string.patch_selection_reset_toast))
}
fun getCustomSelection(): PatchesSelection? {
fun getCustomSelection(): PatchSelection? {
// Convert persistent collections to standard hash collections because persistent collections are not parcelable.
return customPatchesSelection?.mapValues { (_, v) -> v.toSet() }
return customPatchSelection?.mapValues { (_, v) -> v.toSet() }
}
fun getOptions(): Options {
@ -211,20 +210,20 @@ class PatchesSelectorViewModel(input: Params) : ViewModel(), KoinComponent {
)
)
private val patchesSaver: Saver<PersistentPatchesSelection?, Nullable<PatchesSelection>> =
private val selectionSaver: Saver<PersistentPatchSelection?, Nullable<PatchSelection>> =
nullableSaver(persistentMapSaver(valueSaver = persistentSetSaver()))
}
data class Params(
val app: SelectedApp,
val currentSelection: PatchesSelection?,
val currentSelection: PatchSelection?,
val options: Options,
)
}
// Versions of other types, but utilizing persistent/observable collection types.
private typealias PersistentOptions = SnapshotStateMap<Int, PersistentMap<String, PersistentMap<String, Any?>>>
private typealias PersistentPatchesSelection = PersistentMap<Int, PersistentSet<String>>
private typealias PersistentPatchSelection = PersistentMap<Int, PersistentSet<String>>
private fun PatchesSelection.toPersistentPatchesSelection(): PersistentPatchesSelection =
private fun PatchSelection.toPersistentPatchSelection(): PersistentPatchSelection =
mapValues { (_, v) -> v.toPersistentSet() }.toPersistentMap()

View file

@ -20,7 +20,7 @@ import app.revanced.manager.ui.model.BundleInfo.Extensions.toPatchSelection
import app.revanced.manager.ui.model.SelectedApp
import app.revanced.manager.util.Options
import app.revanced.manager.util.PM
import app.revanced.manager.util.PatchesSelection
import app.revanced.manager.util.PatchSelection
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -109,11 +109,11 @@ class SelectedAppInfoViewModel(input: Params) : ViewModel(), KoinComponent {
fun getCustomPatches(
bundles: List<BundleInfo>,
allowUnsupported: Boolean
): PatchesSelection? =
): PatchSelection? =
(selectionState as? SelectionState.Customized)?.patches(bundles, allowUnsupported)
fun updateConfiguration(
selection: PatchesSelection?,
selection: PatchSelection?,
options: Options,
bundles: List<BundleInfo>
) {
@ -135,7 +135,7 @@ class SelectedAppInfoViewModel(input: Params) : ViewModel(), KoinComponent {
data class Params(
val app: SelectedApp,
val patches: PatchesSelection?,
val patches: PatchSelection?,
)
private companion object {
@ -165,15 +165,15 @@ class SelectedAppInfoViewModel(input: Params) : ViewModel(), KoinComponent {
}
private sealed interface SelectionState : Parcelable {
fun patches(bundles: List<BundleInfo>, allowUnsupported: Boolean): PatchesSelection
fun patches(bundles: List<BundleInfo>, allowUnsupported: Boolean): PatchSelection
@Parcelize
data class Customized(val patchesSelection: PatchesSelection) : SelectionState {
data class Customized(val patchSelection: PatchSelection) : SelectionState {
override fun patches(bundles: List<BundleInfo>, allowUnsupported: Boolean) =
bundles.toPatchSelection(
allowUnsupported
) { uid, patch ->
patchesSelection[uid]?.contains(patch.name) ?: false
patchSelection[uid]?.contains(patch.name) ?: false
}
}

View file

@ -34,7 +34,7 @@ import java.time.format.DateTimeFormatter
import java.time.format.DateTimeParseException
import java.util.Locale
typealias PatchesSelection = Map<Int, Set<String>>
typealias PatchSelection = Map<Int, Set<String>>
typealias Options = Map<Int, Map<String, Map<String, Any?>>>
val Context.isDebuggable get() = 0 != applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE

View file

@ -1,13 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="patches_count">
<plurals name="patch_count">
<item quantity="one">%d patch</item>
<item quantity="other">%d patches</item>
</plurals>
<plurals name="applied_patches">
<item quantity="one">%d applied patch</item>
<item quantity="other">%d applied patches</item>
</plurals>
<plurals name="patches_applied">
<item quantity="one">Applied %d patch</item>
<item quantity="other">Applied %d patches</item>

View file

@ -83,22 +83,20 @@
<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="patches_selection">Patches selection</string>
<string name="restore_patches_selection">Restore patches selections</string>
<string name="restore_patches_selection_description">Restore patches selection from a file</string>
<string name="restore_patches_selection_fail">Failed to restore patches selection: %s</string>
<string name="backup_patches_selection">Backup patches selections</string>
<string name="backup_patches_selection_description">Backup patches selection to a file</string>
<string name="backup_patches_selection_fail">Failed to backup patches selection: %s</string>
<string name="clear_patches_selection">Clear patches selection</string>
<string name="clear_patches_selection_description">Clear all patches selection</string>
<string name="patch_options">Patch options</string>
<string name="patch_options_clear_package">Clear patch options for package</string>
<string name="patch_options_clear_package_description">Resets patch options for a single package</string>
<string name="patch_options_clear_bundle">Clear patch options for bundle</string>
<string name="patch_options_clear_bundle_description">Resets patch options for all patches in a bundle</string>
<string name="patch_options_clear_all">Clear all patch options</string>
<string name="patch_options_clear_all_description">Resets all patch options</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="export_patch_selection">Export patch selection</string>
<string name="export_patch_selection_description">Export patch selection from a JSON file</string>
<string name="export_patch_selection_fail">Could not export patch selection: %s</string>
<string name="reset_patch_selection">Reset patch selection</string>
<string name="reset_patch_selection_description">Reset the stored patch selection</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="prefer_splits">Prefer split APK\'s</string>
<string name="prefer_splits_description">Prefer split APK\'s instead of full APK\'s</string>
<string name="prefer_universal">Prefer universal APK\'s</string>
@ -211,8 +209,8 @@
<string name="downloadable_versions">Downloadable versions</string>
<string name="already_patched">Already patched</string>
<string name="patches_selector_sheet_filter_title">Filter</string>
<string name="patches_selector_sheet_filter_compat_title">Compatibility</string>
<string name="patch_selector_sheet_filter_title">Filter</string>
<string name="patch_selector_sheet_filter_compat_title">Compatibility</string>
<string name="string_option_icon_description">Edit</string>
<string name="string_option_menu_description">More options</string>