From 5762859906f534232ecbbf64dafe02bbd049fc80 Mon Sep 17 00:00:00 2001 From: Ax333l Date: Fri, 13 Oct 2023 18:11:40 +0200 Subject: [PATCH] feat: add patches selector bottom sheet (#1360) --- .../ui/screen/PatchesSelectorScreen.kt | 346 +++++++++++------- app/src/main/res/values/strings.xml | 6 +- 2 files changed, 224 insertions(+), 128 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt index 36d12f23..01cda7c0 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt @@ -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, + 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.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, - 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 ) }, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 58c4b940..381ae555 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -143,8 +143,6 @@ Unsupported app Unsupported patches Universal patches - Use default selection - Use previous selection Patch selection and options has been reset to recommended defaults Stop using defaults? You may encounter issues when not using the default patch selection and options. @@ -152,6 +150,7 @@ Supported Universal Unsupported + Patch name Some of the patches do not support this app version (%1$s). The patches only support the following version(s): %2$s. Continue with this version? Not all patches support this version (%s). Do you want to continue anyway? @@ -188,6 +187,9 @@ Downloadable versions Already patched + Filter + Compatibility + Edit More options Value