mirror of
https://github.com/revanced/revanced-manager-compose.git
synced 2025-04-30 06:14:25 +02:00
feat: filter options for patches
This commit is contained in:
parent
56bc4ba7f1
commit
41c521876a
@ -2,7 +2,12 @@ package app.revanced.manager.ui.screen
|
|||||||
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.pager.HorizontalPager
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
@ -12,7 +17,21 @@ import androidx.compose.material.icons.filled.Build
|
|||||||
import androidx.compose.material.icons.outlined.HelpOutline
|
import androidx.compose.material.icons.outlined.HelpOutline
|
||||||
import androidx.compose.material.icons.outlined.Search
|
import androidx.compose.material.icons.outlined.Search
|
||||||
import androidx.compose.material.icons.outlined.Settings
|
import androidx.compose.material.icons.outlined.Settings
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Checkbox
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
|
import androidx.compose.material3.FilterChip
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.ListItem
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.ScrollableTabRow
|
||||||
|
import androidx.compose.material3.Tab
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
@ -24,8 +43,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.patcher.patch.PatchInfo
|
import app.revanced.manager.patcher.patch.PatchInfo
|
||||||
import app.revanced.manager.ui.component.AppTopBar
|
import app.revanced.manager.ui.component.AppTopBar
|
||||||
import app.revanced.manager.ui.component.GroupHeader
|
|
||||||
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel
|
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel
|
||||||
|
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel.Companion.SHOW_SUPPORTED
|
||||||
|
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.PatchesSelection
|
import app.revanced.manager.util.PatchesSelection
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@ -43,27 +64,43 @@ fun PatchesSelectorScreen(
|
|||||||
|
|
||||||
val bundles by vm.bundlesFlow.collectAsStateWithLifecycle(initialValue = emptyList())
|
val bundles by vm.bundlesFlow.collectAsStateWithLifecycle(initialValue = emptyList())
|
||||||
|
|
||||||
if (vm.showUnsupportedDialog) UnsupportedDialog(onDismissRequest = vm::dismissDialogs)
|
if (vm.compatibleVersions.isNotEmpty())
|
||||||
|
UnsupportedDialog(
|
||||||
|
appVersion = vm.appInfo.packageInfo!!.versionName,
|
||||||
|
supportedVersions = vm.compatibleVersions,
|
||||||
|
onDismissRequest = vm::dismissDialogs
|
||||||
|
)
|
||||||
|
|
||||||
if (vm.showOptionsDialog) OptionsDialog(onDismissRequest = vm::dismissDialogs, onConfirm = {})
|
if (vm.showOptionsDialog) OptionsDialog(onDismissRequest = vm::dismissDialogs, onConfirm = {})
|
||||||
|
|
||||||
Scaffold(topBar = {
|
Scaffold(
|
||||||
AppTopBar(title = stringResource(R.string.select_patches), onBackClick = onBackClick, actions = {
|
topBar = {
|
||||||
|
AppTopBar(
|
||||||
|
title = stringResource(R.string.select_patches),
|
||||||
|
onBackClick = onBackClick,
|
||||||
|
actions = {
|
||||||
IconButton(onClick = { }) {
|
IconButton(onClick = { }) {
|
||||||
Icon(Icons.Outlined.HelpOutline, stringResource(R.string.help))
|
Icon(Icons.Outlined.HelpOutline, stringResource(R.string.help))
|
||||||
}
|
}
|
||||||
IconButton(onClick = { }) {
|
IconButton(onClick = { }) {
|
||||||
Icon(Icons.Outlined.Search, stringResource(R.string.search))
|
Icon(Icons.Outlined.Search, stringResource(R.string.search))
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}, floatingActionButton = {
|
)
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
ExtendedFloatingActionButton(text = { Text(stringResource(R.string.patch)) },
|
ExtendedFloatingActionButton(text = { Text(stringResource(R.string.patch)) },
|
||||||
icon = { Icon(Icons.Default.Build, null) },
|
icon = { Icon(Icons.Default.Build, null) },
|
||||||
onClick = { onPatchClick(vm.generateSelection()) })
|
onClick = { onPatchClick(vm.generateSelection()) })
|
||||||
}) { paddingValues ->
|
}
|
||||||
Column(Modifier.fillMaxSize().padding(paddingValues)) {
|
) { paddingValues ->
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(paddingValues)
|
||||||
|
) {
|
||||||
if (bundles.size > 1) {
|
if (bundles.size > 1) {
|
||||||
TabRow(
|
ScrollableTabRow(
|
||||||
selectedTabIndex = pagerState.currentPage,
|
selectedTabIndex = pagerState.currentPage,
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp)
|
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp)
|
||||||
) {
|
) {
|
||||||
@ -85,57 +122,98 @@ fun PatchesSelectorScreen(
|
|||||||
userScrollEnabled = true,
|
userScrollEnabled = true,
|
||||||
pageContent = { index ->
|
pageContent = { index ->
|
||||||
|
|
||||||
val (bundleName, supportedPatches, unsupportedPatches) = bundles[index]
|
val (bundleName, supportedPatches, unsupportedPatches, universalPatches) = bundles[index]
|
||||||
|
|
||||||
|
Column {
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 10.dp, vertical = 2.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(5.dp)
|
||||||
|
) {
|
||||||
|
FilterChip(
|
||||||
|
selected = vm.filter and SHOW_SUPPORTED != 0,
|
||||||
|
onClick = { vm.toggleFlag(SHOW_SUPPORTED) },
|
||||||
|
label = { Text(stringResource(R.string.supported)) }
|
||||||
|
)
|
||||||
|
|
||||||
|
FilterChip(
|
||||||
|
selected = vm.filter and SHOW_UNIVERSAL != 0,
|
||||||
|
onClick = { vm.toggleFlag(SHOW_UNIVERSAL) },
|
||||||
|
label = { Text(stringResource(R.string.universal)) }
|
||||||
|
)
|
||||||
|
|
||||||
|
FilterChip(
|
||||||
|
selected = vm.filter and SHOW_UNSUPPORTED != 0,
|
||||||
|
onClick = { vm.toggleFlag(SHOW_UNSUPPORTED) },
|
||||||
|
label = { Text(stringResource(R.string.unsupported)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
|
if (supportedPatches.isNotEmpty() && (vm.filter and SHOW_SUPPORTED != 0 || vm.filter == 0)) {
|
||||||
|
|
||||||
items(
|
items(
|
||||||
items = supportedPatches
|
items = supportedPatches,
|
||||||
|
key = { it.name }
|
||||||
) { patch ->
|
) { patch ->
|
||||||
PatchItem(
|
PatchItem(
|
||||||
patch = patch,
|
patch = patch,
|
||||||
onOptionsDialog = vm::openOptionsDialog,
|
onOptionsDialog = vm::openOptionsDialog,
|
||||||
onToggle = {
|
onToggle = { vm.togglePatch(bundleName, patch) },
|
||||||
vm.togglePatch(bundleName, patch)
|
selected = vm.isSelected(bundleName, patch)
|
||||||
},
|
)
|
||||||
selected = vm.isSelected(bundleName, patch),
|
}
|
||||||
supported = true
|
}
|
||||||
|
|
||||||
|
if (universalPatches.isNotEmpty() && (vm.filter and SHOW_UNIVERSAL != 0 || vm.filter == 0)) {
|
||||||
|
item {
|
||||||
|
ListHeader(
|
||||||
|
title = stringResource(R.string.universal_patches),
|
||||||
|
onHelpClick = { }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unsupportedPatches.isNotEmpty()) {
|
items(
|
||||||
item {
|
items = universalPatches,
|
||||||
Row(
|
key = { it.name }
|
||||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 14.dp).padding(end = 10.dp),
|
) { patch ->
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
PatchItem(
|
||||||
) {
|
patch = patch,
|
||||||
GroupHeader(stringResource(R.string.unsupported_patches), Modifier.padding(0.dp))
|
onOptionsDialog = vm::openOptionsDialog,
|
||||||
IconButton(onClick = vm::openUnsupportedDialog) {
|
onToggle = { vm.togglePatch(bundleName, patch) },
|
||||||
Icon(
|
selected = vm.isSelected(bundleName, patch)
|
||||||
Icons.Outlined.HelpOutline, stringResource(R.string.help)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (unsupportedPatches.isNotEmpty() && (vm.filter and SHOW_UNSUPPORTED != 0 || vm.filter == 0)) {
|
||||||
|
item {
|
||||||
|
ListHeader(
|
||||||
|
title = stringResource(R.string.unsupported_patches),
|
||||||
|
onHelpClick = { vm.openUnsupportedDialog(unsupportedPatches) }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
items(
|
items(
|
||||||
items = unsupportedPatches,
|
items = unsupportedPatches,
|
||||||
// key = { it.name }
|
key = { it.name }
|
||||||
) { patch ->
|
) { patch ->
|
||||||
PatchItem(
|
PatchItem(
|
||||||
patch = patch,
|
patch = patch,
|
||||||
onOptionsDialog = vm::openOptionsDialog,
|
onOptionsDialog = vm::openOptionsDialog,
|
||||||
onToggle = {
|
onToggle = { vm.togglePatch(bundleName, patch) },
|
||||||
vm.togglePatch(bundleName, patch)
|
|
||||||
},
|
|
||||||
selected = vm.isSelected(bundleName, patch),
|
selected = vm.isSelected(bundleName, patch),
|
||||||
supported = allowUnsupported
|
supported = allowUnsupported
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,27 +225,21 @@ fun PatchItem(
|
|||||||
onOptionsDialog: () -> Unit,
|
onOptionsDialog: () -> Unit,
|
||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
onToggle: () -> Unit,
|
onToggle: () -> Unit,
|
||||||
supported: Boolean
|
supported: Boolean = true
|
||||||
) {
|
) = ListItem(
|
||||||
ListItem(
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.let { if (!supported) it.alpha(0.5f) else it }
|
.let { if (!supported) it.alpha(0.5f) else it }
|
||||||
.clickable(enabled = supported, onClick = onToggle),
|
.clickable(enabled = supported, onClick = onToggle)
|
||||||
|
.fillMaxSize(),
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
Checkbox(
|
Checkbox(
|
||||||
checked = selected,
|
checked = selected,
|
||||||
onCheckedChange = {
|
onCheckedChange = null,
|
||||||
onToggle()
|
|
||||||
},
|
|
||||||
enabled = supported
|
enabled = supported
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
headlineContent = {
|
headlineContent = { Text(patch.name) },
|
||||||
Text(patch.name)
|
supportingContent = patch.description?.let { { Text(it) } },
|
||||||
},
|
|
||||||
supportingContent = {
|
|
||||||
Text(patch.description ?: "")
|
|
||||||
},
|
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
if (patch.options?.isNotEmpty() == true) {
|
if (patch.options?.isNotEmpty() == true) {
|
||||||
IconButton(onClick = onOptionsDialog, enabled = supported) {
|
IconButton(onClick = onOptionsDialog, enabled = supported) {
|
||||||
@ -176,17 +248,37 @@ fun PatchItem(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ListHeader(
|
||||||
|
title: String,
|
||||||
|
onHelpClick: (() -> Unit)? = null
|
||||||
|
) {
|
||||||
|
ListItem(
|
||||||
|
headlineContent = {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
style = MaterialTheme.typography.labelLarge
|
||||||
|
)
|
||||||
|
},
|
||||||
|
trailingContent = onHelpClick?.let { {
|
||||||
|
IconButton(onClick = onHelpClick) {
|
||||||
|
Icon(
|
||||||
|
Icons.Outlined.HelpOutline,
|
||||||
|
stringResource(R.string.help)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UnsupportedDialog(
|
fun UnsupportedDialog(
|
||||||
|
appVersion: String,
|
||||||
|
supportedVersions: List<String>,
|
||||||
onDismissRequest: () -> Unit
|
onDismissRequest: () -> Unit
|
||||||
) {
|
) = AlertDialog(
|
||||||
val appVersion = "1.1.0"
|
|
||||||
val supportedVersions =
|
|
||||||
listOf("1.1.1", "1.2.0", "1.1.1", "1.2.0", "1.1.1", "1.2.0", "1.1.1", "1.2.0", "1.1.1", "1.2.0")
|
|
||||||
|
|
||||||
AlertDialog(modifier = Modifier.padding(vertical = 45.dp),
|
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(onClick = onDismissRequest) {
|
TextButton(onClick = onDismissRequest) {
|
||||||
@ -194,21 +286,27 @@ fun UnsupportedDialog(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
title = { Text(stringResource(R.string.unsupported_app)) },
|
title = { Text(stringResource(R.string.unsupported_app)) },
|
||||||
text = { Text(stringResource(R.string.app_not_supported, appVersion, supportedVersions.joinToString(", "))) })
|
text = { Text(stringResource(R.string.app_not_supported, appVersion, supportedVersions.joinToString(", "))) }
|
||||||
}
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun OptionsDialog(
|
fun OptionsDialog(
|
||||||
onDismissRequest: () -> Unit, onConfirm: () -> Unit
|
onDismissRequest: () -> Unit, onConfirm: () -> Unit
|
||||||
) {
|
) = AlertDialog(
|
||||||
AlertDialog(onDismissRequest = onDismissRequest, confirmButton = {
|
onDismissRequest = onDismissRequest,
|
||||||
Button(onClick = {
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismissRequest) {
|
||||||
|
Text(stringResource(R.string.cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = {
|
||||||
onConfirm()
|
onConfirm()
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
}) {
|
}) {
|
||||||
Text(stringResource(R.string.apply))
|
Text(stringResource(R.string.apply))
|
||||||
}
|
}
|
||||||
}, title = { Text(stringResource(R.string.options)) }, text = {
|
},
|
||||||
Text("You really thought these would exist?")
|
title = { Text(stringResource(R.string.options)) },
|
||||||
})
|
text = { Text("You really thought these would exist?") }
|
||||||
}
|
)
|
@ -15,19 +15,23 @@ import org.koin.core.component.KoinComponent
|
|||||||
import org.koin.core.component.get
|
import org.koin.core.component.get
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class PatchesSelectorViewModel(appInfo: AppInfo) : ViewModel(), KoinComponent {
|
class PatchesSelectorViewModel(
|
||||||
|
val appInfo: AppInfo
|
||||||
|
) : ViewModel(), KoinComponent {
|
||||||
|
|
||||||
val bundlesFlow = get<SourceRepository>().bundles.map { bundles ->
|
val bundlesFlow = get<SourceRepository>().bundles.map { bundles ->
|
||||||
bundles.mapValues { (_, bundle) -> bundle.patches }.map { (name, patches) ->
|
bundles.mapValues { (_, bundle) -> bundle.patches }.map { (name, patches) ->
|
||||||
val supported = mutableListOf<PatchInfo>()
|
val supported = mutableListOf<PatchInfo>()
|
||||||
val unsupported = mutableListOf<PatchInfo>()
|
val unsupported = mutableListOf<PatchInfo>()
|
||||||
|
val universal = mutableListOf<PatchInfo>()
|
||||||
|
|
||||||
patches.filter { it.compatibleWith(appInfo.packageName) }.forEach {
|
patches.filter { it.compatibleWith(appInfo.packageName) }.forEach {
|
||||||
val targetList = if (it.supportsVersion(appInfo.packageInfo!!.versionName)) supported else unsupported
|
val targetList = if (it.compatiblePackages == null) universal else if (it.supportsVersion(appInfo.packageInfo!!.versionName)) supported else unsupported
|
||||||
|
|
||||||
targetList.add(it)
|
targetList.add(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
Bundle(name, supported, unsupported)
|
Bundle(name, supported, unsupported, universal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,24 +49,48 @@ class PatchesSelectorViewModel(appInfo: AppInfo) : ViewModel(), KoinComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class Bundle(
|
data class Bundle(
|
||||||
val name: String, val supported: List<PatchInfo>, val unsupported: List<PatchInfo>
|
val name: String,
|
||||||
|
val supported: List<PatchInfo>,
|
||||||
|
val unsupported: List<PatchInfo>,
|
||||||
|
val universal: List<PatchInfo>
|
||||||
)
|
)
|
||||||
|
|
||||||
var showOptionsDialog by mutableStateOf(false)
|
var showOptionsDialog by mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
var showUnsupportedDialog by mutableStateOf(false)
|
|
||||||
private set
|
val compatibleVersions = mutableStateListOf<String>()
|
||||||
|
|
||||||
fun dismissDialogs() {
|
fun dismissDialogs() {
|
||||||
showOptionsDialog = false
|
showOptionsDialog = false
|
||||||
showUnsupportedDialog = false
|
compatibleVersions.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openOptionsDialog() {
|
fun openOptionsDialog() {
|
||||||
showOptionsDialog = true
|
showOptionsDialog = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openUnsupportedDialog() {
|
fun openUnsupportedDialog(unsupportedVersions: List<PatchInfo>) {
|
||||||
showUnsupportedDialog = true
|
val set = HashSet<String>()
|
||||||
|
|
||||||
|
unsupportedVersions.forEach { patch ->
|
||||||
|
patch.compatiblePackages?.find { it.name == appInfo.packageName }?.let { compatiblePackage ->
|
||||||
|
set.addAll(compatiblePackage.versions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compatibleVersions.addAll(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
var filter by mutableStateOf(SHOW_SUPPORTED or SHOW_UNSUPPORTED)
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun toggleFlag(flag: Int) {
|
||||||
|
filter = filter xor flag
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val SHOW_SUPPORTED = 1 // 2^0
|
||||||
|
const val SHOW_UNIVERSAL = 2 // 2^1
|
||||||
|
const val SHOW_UNSUPPORTED = 4 // 2^2
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -67,6 +67,10 @@
|
|||||||
<string name="no_patched_apps_found">No patched apps found</string>
|
<string name="no_patched_apps_found">No patched apps found</string>
|
||||||
<string name="unsupported_app">Unsupported app</string>
|
<string name="unsupported_app">Unsupported app</string>
|
||||||
<string name="unsupported_patches">Unsupported patches</string>
|
<string name="unsupported_patches">Unsupported patches</string>
|
||||||
|
<string name="universal_patches">Universal patches</string>
|
||||||
|
<string name="supported">Supported</string>
|
||||||
|
<string name="universal">Universal</string>
|
||||||
|
<string name="unsupported">Unsupported</string>
|
||||||
<string name="app_not_supported">Some of the patches do not support this app version (%1$s). The patches only support the following versions: %2$s.</string>
|
<string name="app_not_supported">Some of the patches do not support this app version (%1$s). The patches only support the following versions: %2$s.</string>
|
||||||
|
|
||||||
<string name="select_file">Select file</string>
|
<string name="select_file">Select file</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user