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.HorizontalPager
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.material.icons.Icons
|
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.filled.Build
|
||||||
|
import androidx.compose.material.icons.outlined.FilterList
|
||||||
import androidx.compose.material.icons.outlined.HelpOutline
|
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.Restore
|
||||||
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.material.icons.outlined.WarningAmber
|
import androidx.compose.material.icons.outlined.WarningAmber
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
import androidx.compose.material3.FilterChip
|
import androidx.compose.material3.FilterChip
|
||||||
@ -32,8 +32,10 @@ import androidx.compose.material3.Icon
|
|||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.ScrollableTabRow
|
import androidx.compose.material3.ScrollableTabRow
|
||||||
|
import androidx.compose.material3.SearchBar
|
||||||
import androidx.compose.material3.Tab
|
import androidx.compose.material3.Tab
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
@ -79,9 +81,86 @@ fun PatchesSelectorScreen(
|
|||||||
) {
|
) {
|
||||||
val pagerState = rememberPagerState()
|
val pagerState = rememberPagerState()
|
||||||
val composableScope = rememberCoroutineScope()
|
val composableScope = rememberCoroutineScope()
|
||||||
|
var search: String? by rememberSaveable {
|
||||||
|
mutableStateOf(null)
|
||||||
|
}
|
||||||
|
var showBottomSheet by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
val bundles by vm.bundlesFlow.collectAsStateWithLifecycle(initialValue = emptyList())
|
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())
|
if (vm.compatibleVersions.isNotEmpty())
|
||||||
UnsupportedDialog(
|
UnsupportedDialog(
|
||||||
appVersion = vm.input.selectedApp.version,
|
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(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
AppTopBar(
|
AppTopBar(
|
||||||
@ -115,34 +301,14 @@ fun PatchesSelectorScreen(
|
|||||||
IconButton(onClick = vm::reset) {
|
IconButton(onClick = vm::reset) {
|
||||||
Icon(Icons.Outlined.Restore, stringResource(R.string.reset))
|
Icon(Icons.Outlined.Restore, stringResource(R.string.reset))
|
||||||
}
|
}
|
||||||
|
IconButton(onClick = { showBottomSheet = true }) {
|
||||||
var dropdownActive by rememberSaveable {
|
Icon(Icons.Outlined.FilterList, stringResource(R.string.more))
|
||||||
mutableStateOf(false)
|
|
||||||
}
|
}
|
||||||
// This part should probably be changed
|
IconButton(
|
||||||
IconButton(onClick = { dropdownActive = true }) {
|
onClick = {
|
||||||
Icon(Icons.Outlined.MoreVert, stringResource(R.string.more))
|
search = ""
|
||||||
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 = { }) {
|
|
||||||
Icon(Icons.Outlined.Search, stringResource(R.string.search))
|
Icon(Icons.Outlined.Search, stringResource(R.string.search))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,107 +364,35 @@ fun PatchesSelectorScreen(
|
|||||||
pageContent = { index ->
|
pageContent = { index ->
|
||||||
val bundle = bundles[index]
|
val bundle = bundles[index]
|
||||||
|
|
||||||
Column {
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
Row(
|
) {
|
||||||
modifier = Modifier
|
patchList(
|
||||||
.fillMaxWidth()
|
uid = bundle.uid,
|
||||||
.padding(horizontal = 10.dp, vertical = 2.dp),
|
patches = bundle.supported,
|
||||||
horizontalArrangement = Arrangement.spacedBy(5.dp)
|
filterFlag = SHOW_SUPPORTED,
|
||||||
|
supported = true
|
||||||
|
)
|
||||||
|
patchList(
|
||||||
|
uid = bundle.uid,
|
||||||
|
patches = bundle.universal,
|
||||||
|
filterFlag = SHOW_UNIVERSAL,
|
||||||
|
supported = true
|
||||||
) {
|
) {
|
||||||
FilterChip(
|
ListHeader(
|
||||||
selected = vm.filter and SHOW_SUPPORTED != 0 && bundle.supported.isNotEmpty(),
|
title = stringResource(R.string.universal_patches),
|
||||||
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()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
patchList(
|
||||||
val allowExperimental by vm.allowExperimental.getAsState()
|
uid = bundle.uid,
|
||||||
|
patches = bundle.unsupported,
|
||||||
LazyColumn(
|
filterFlag = SHOW_UNSUPPORTED,
|
||||||
modifier = Modifier.fillMaxSize()
|
supported = allowExperimental
|
||||||
) {
|
) {
|
||||||
fun LazyListScope.patchList(
|
ListHeader(
|
||||||
patches: List<PatchInfo>,
|
title = stringResource(R.string.unsupported_patches),
|
||||||
filterFlag: Int,
|
onHelpClick = { vm.openUnsupportedDialog(bundle.unsupported) }
|
||||||
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
|
|
||||||
)
|
)
|
||||||
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 = {
|
leadingContent = {
|
||||||
Checkbox(
|
Checkbox(
|
||||||
checked = selected,
|
checked = selected,
|
||||||
onCheckedChange = null,
|
onCheckedChange = { onToggle() },
|
||||||
enabled = supported
|
enabled = supported
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -143,8 +143,6 @@
|
|||||||
<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="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="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_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_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="supported">Supported</string>
|
||||||
<string name="universal">Universal</string>
|
<string name="universal">Universal</string>
|
||||||
<string name="unsupported">Unsupported</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="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="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>
|
<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="downloadable_versions">Downloadable versions</string>
|
||||||
<string name="already_patched">Already patched</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_icon_description">Edit</string>
|
||||||
<string name="string_option_menu_description">More options</string>
|
<string name="string_option_menu_description">More options</string>
|
||||||
<string name="string_option_placeholder">Value</string>
|
<string name="string_option_placeholder">Value</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user