mirror of
https://github.com/revanced/revanced-manager
synced 2024-05-14 13:56:57 +02:00
feat: add patches selector bottom sheet (#1360)
This commit is contained in:
parent
608bac6854
commit
5762859906
@ -14,17 +14,17 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Build
|
||||
import androidx.compose.material.icons.outlined.FilterList
|
||||
import androidx.compose.material.icons.outlined.HelpOutline
|
||||
import androidx.compose.material.icons.outlined.MoreVert
|
||||
import androidx.compose.material.icons.outlined.Restore
|
||||
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.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.FilterChip
|
||||
@ -32,8 +32,10 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.ScrollableTabRow
|
||||
import androidx.compose.material3.SearchBar
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
@ -79,9 +81,86 @@ fun PatchesSelectorScreen(
|
||||
) {
|
||||
val pagerState = rememberPagerState()
|
||||
val composableScope = rememberCoroutineScope()
|
||||
var search: String? by rememberSaveable {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
var showBottomSheet by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val bundles by vm.bundlesFlow.collectAsStateWithLifecycle(initialValue = emptyList())
|
||||
|
||||
if (showBottomSheet) {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = {
|
||||
showBottomSheet = false
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = 24.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.patches_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),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
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)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
ListItem(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(
|
||||
enabled = vm.hasPreviousSelection,
|
||||
onClick = vm::switchBaseSelectionMode
|
||||
),
|
||||
leadingContent = {
|
||||
Checkbox(
|
||||
checked = vm.baseSelectionMode == BaseSelectionMode.PREVIOUS,
|
||||
onCheckedChange = {
|
||||
vm.switchBaseSelectionMode()
|
||||
},
|
||||
enabled = vm.hasPreviousSelection
|
||||
)
|
||||
},
|
||||
headlineContent = {
|
||||
Text(
|
||||
"Use previous selection",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (vm.compatibleVersions.isNotEmpty())
|
||||
UnsupportedDialog(
|
||||
appVersion = vm.input.selectedApp.version,
|
||||
@ -106,6 +185,113 @@ fun PatchesSelectorScreen(
|
||||
)
|
||||
}
|
||||
|
||||
val allowExperimental by vm.allowExperimental.getAsState()
|
||||
|
||||
fun LazyListScope.patchList(
|
||||
uid: Int,
|
||||
patches: List<PatchInfo>,
|
||||
filterFlag: Int,
|
||||
supported: Boolean,
|
||||
header: (@Composable () -> Unit)? = null
|
||||
) {
|
||||
if (patches.isNotEmpty() && (vm.filter and filterFlag) != 0 || vm.filter == 0) {
|
||||
header?.let {
|
||||
item {
|
||||
it()
|
||||
}
|
||||
}
|
||||
|
||||
items(
|
||||
items = patches,
|
||||
key = { it.name }
|
||||
) { patch ->
|
||||
PatchItem(
|
||||
patch = patch,
|
||||
onOptionsDialog = {
|
||||
vm.optionsDialog = uid to patch
|
||||
},
|
||||
selected = supported && vm.isSelected(
|
||||
uid,
|
||||
patch
|
||||
),
|
||||
onToggle = {
|
||||
if (vm.selectionWarningEnabled) {
|
||||
vm.pendingSelectionAction = {
|
||||
vm.togglePatch(uid, patch)
|
||||
}
|
||||
} else {
|
||||
vm.togglePatch(uid, patch)
|
||||
}
|
||||
},
|
||||
supported = supported
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
search?.let { query ->
|
||||
SearchBar(
|
||||
query = query,
|
||||
onQueryChange = { new ->
|
||||
search = new
|
||||
},
|
||||
onSearch = {},
|
||||
active = true,
|
||||
onActiveChange = { new ->
|
||||
if (new) return@SearchBar
|
||||
search = null
|
||||
},
|
||||
placeholder = {
|
||||
Text(stringResource(R.string.search_patches))
|
||||
},
|
||||
leadingIcon = {
|
||||
IconButton(onClick = { search = null }) {
|
||||
Icon(
|
||||
Icons.Default.ArrowBack,
|
||||
stringResource(R.string.back)
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
val bundle = bundles[pagerState.currentPage]
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
fun List<PatchInfo>.searched() = filter {
|
||||
it.name.contains(query, true)
|
||||
}
|
||||
|
||||
patchList(
|
||||
uid = bundle.uid,
|
||||
patches = bundle.supported.searched(),
|
||||
filterFlag = SHOW_SUPPORTED,
|
||||
supported = true
|
||||
)
|
||||
patchList(
|
||||
uid = bundle.uid,
|
||||
patches = bundle.universal.searched(),
|
||||
filterFlag = SHOW_UNIVERSAL,
|
||||
supported = true
|
||||
) {
|
||||
ListHeader(
|
||||
title = stringResource(R.string.universal_patches),
|
||||
)
|
||||
}
|
||||
|
||||
if (!allowExperimental) return@LazyColumn
|
||||
patchList(
|
||||
uid = bundle.uid,
|
||||
patches = bundle.unsupported.searched(),
|
||||
filterFlag = SHOW_UNSUPPORTED,
|
||||
supported = true
|
||||
) {
|
||||
ListHeader(
|
||||
title = stringResource(R.string.unsupported_patches),
|
||||
onHelpClick = { vm.openUnsupportedDialog(bundle.unsupported) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
AppTopBar(
|
||||
@ -115,34 +301,14 @@ fun PatchesSelectorScreen(
|
||||
IconButton(onClick = vm::reset) {
|
||||
Icon(Icons.Outlined.Restore, stringResource(R.string.reset))
|
||||
}
|
||||
|
||||
var dropdownActive by rememberSaveable {
|
||||
mutableStateOf(false)
|
||||
IconButton(onClick = { showBottomSheet = true }) {
|
||||
Icon(Icons.Outlined.FilterList, stringResource(R.string.more))
|
||||
}
|
||||
// This part should probably be changed
|
||||
IconButton(onClick = { dropdownActive = true }) {
|
||||
Icon(Icons.Outlined.MoreVert, stringResource(R.string.more))
|
||||
DropdownMenu(
|
||||
expanded = dropdownActive,
|
||||
onDismissRequest = { dropdownActive = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
val id =
|
||||
if (vm.baseSelectionMode == BaseSelectionMode.DEFAULT)
|
||||
R.string.menu_opt_selection_mode_previous else R.string.menu_opt_selection_mode_default
|
||||
|
||||
Text(stringResource(id))
|
||||
},
|
||||
onClick = {
|
||||
dropdownActive = false
|
||||
vm.switchBaseSelectionMode()
|
||||
},
|
||||
enabled = vm.hasPreviousSelection
|
||||
)
|
||||
IconButton(
|
||||
onClick = {
|
||||
search = ""
|
||||
}
|
||||
}
|
||||
IconButton(onClick = { }) {
|
||||
) {
|
||||
Icon(Icons.Outlined.Search, stringResource(R.string.search))
|
||||
}
|
||||
}
|
||||
@ -198,107 +364,35 @@ fun PatchesSelectorScreen(
|
||||
pageContent = { index ->
|
||||
val bundle = bundles[index]
|
||||
|
||||
Column {
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 10.dp, vertical = 2.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(5.dp)
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
patchList(
|
||||
uid = bundle.uid,
|
||||
patches = bundle.supported,
|
||||
filterFlag = SHOW_SUPPORTED,
|
||||
supported = true
|
||||
)
|
||||
patchList(
|
||||
uid = bundle.uid,
|
||||
patches = bundle.universal,
|
||||
filterFlag = SHOW_UNIVERSAL,
|
||||
supported = true
|
||||
) {
|
||||
FilterChip(
|
||||
selected = vm.filter and SHOW_SUPPORTED != 0 && bundle.supported.isNotEmpty(),
|
||||
onClick = { vm.toggleFlag(SHOW_SUPPORTED) },
|
||||
label = { Text(stringResource(R.string.supported)) },
|
||||
enabled = bundle.supported.isNotEmpty()
|
||||
)
|
||||
|
||||
FilterChip(
|
||||
selected = vm.filter and SHOW_UNIVERSAL != 0 && bundle.universal.isNotEmpty(),
|
||||
onClick = { vm.toggleFlag(SHOW_UNIVERSAL) },
|
||||
label = { Text(stringResource(R.string.universal)) },
|
||||
enabled = bundle.universal.isNotEmpty()
|
||||
)
|
||||
|
||||
FilterChip(
|
||||
selected = vm.filter and SHOW_UNSUPPORTED != 0 && bundle.unsupported.isNotEmpty(),
|
||||
onClick = { vm.toggleFlag(SHOW_UNSUPPORTED) },
|
||||
label = { Text(stringResource(R.string.unsupported)) },
|
||||
enabled = bundle.unsupported.isNotEmpty()
|
||||
ListHeader(
|
||||
title = stringResource(R.string.universal_patches),
|
||||
)
|
||||
}
|
||||
|
||||
val allowExperimental by vm.allowExperimental.getAsState()
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
patchList(
|
||||
uid = bundle.uid,
|
||||
patches = bundle.unsupported,
|
||||
filterFlag = SHOW_UNSUPPORTED,
|
||||
supported = allowExperimental
|
||||
) {
|
||||
fun LazyListScope.patchList(
|
||||
patches: List<PatchInfo>,
|
||||
filterFlag: Int,
|
||||
supported: Boolean,
|
||||
header: (@Composable () -> Unit)? = null
|
||||
) {
|
||||
if (patches.isNotEmpty() && (vm.filter and filterFlag) != 0 || vm.filter == 0) {
|
||||
header?.let {
|
||||
item {
|
||||
it()
|
||||
}
|
||||
}
|
||||
|
||||
items(
|
||||
items = patches,
|
||||
key = { it.name }
|
||||
) { patch ->
|
||||
PatchItem(
|
||||
patch = patch,
|
||||
onOptionsDialog = {
|
||||
vm.optionsDialog = bundle.uid to patch
|
||||
},
|
||||
selected = supported && vm.isSelected(
|
||||
bundle.uid,
|
||||
patch
|
||||
),
|
||||
onToggle = {
|
||||
if (vm.selectionWarningEnabled) {
|
||||
vm.pendingSelectionAction = {
|
||||
vm.togglePatch(bundle.uid, patch)
|
||||
}
|
||||
} else {
|
||||
vm.togglePatch(bundle.uid, patch)
|
||||
}
|
||||
},
|
||||
supported = supported
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
patchList(
|
||||
patches = bundle.supported,
|
||||
filterFlag = SHOW_SUPPORTED,
|
||||
supported = true
|
||||
ListHeader(
|
||||
title = stringResource(R.string.unsupported_patches),
|
||||
onHelpClick = { vm.openUnsupportedDialog(bundle.unsupported) }
|
||||
)
|
||||
patchList(
|
||||
patches = bundle.universal,
|
||||
filterFlag = SHOW_UNIVERSAL,
|
||||
supported = true
|
||||
) {
|
||||
ListHeader(
|
||||
title = stringResource(R.string.universal_patches),
|
||||
onHelpClick = { }
|
||||
)
|
||||
}
|
||||
patchList(
|
||||
patches = bundle.unsupported,
|
||||
filterFlag = SHOW_UNSUPPORTED,
|
||||
supported = allowExperimental
|
||||
) {
|
||||
ListHeader(
|
||||
title = stringResource(R.string.unsupported_patches),
|
||||
onHelpClick = { vm.openUnsupportedDialog(bundle.unsupported) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -400,7 +494,7 @@ fun PatchItem(
|
||||
leadingContent = {
|
||||
Checkbox(
|
||||
checked = selected,
|
||||
onCheckedChange = null,
|
||||
onCheckedChange = { onToggle() },
|
||||
enabled = supported
|
||||
)
|
||||
},
|
||||
|
@ -143,8 +143,6 @@
|
||||
<string name="unsupported_app">Unsupported app</string>
|
||||
<string name="unsupported_patches">Unsupported patches</string>
|
||||
<string name="universal_patches">Universal patches</string>
|
||||
<string name="menu_opt_selection_mode_default">Use default selection</string>
|
||||
<string name="menu_opt_selection_mode_previous">Use previous selection</string>
|
||||
<string name="patch_selection_reset_toast">Patch selection and options has been reset to recommended defaults</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>
|
||||
@ -152,6 +150,7 @@
|
||||
<string name="supported">Supported</string>
|
||||
<string name="universal">Universal</string>
|
||||
<string name="unsupported">Unsupported</string>
|
||||
<string name="search_patches">Patch name</string>
|
||||
<string name="app_not_supported">Some of the patches do not support this app version (%1$s). The patches only support the following version(s): %2$s.</string>
|
||||
<string name="continue_with_version">Continue with this version?</string>
|
||||
<string name="version_not_supported">Not all patches support this version (%s). Do you want to continue anyway?</string>
|
||||
@ -188,6 +187,9 @@
|
||||
<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="string_option_icon_description">Edit</string>
|
||||
<string name="string_option_menu_description">More options</string>
|
||||
<string name="string_option_placeholder">Value</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user