feat: Migrate more buttons

This commit is contained in:
Ushie 2024-07-26 02:27:06 +03:00
parent bfbf27024d
commit 968c665791
No known key found for this signature in database
GPG key ID: B3AAD18842E34632
7 changed files with 306 additions and 272 deletions

View file

@ -10,26 +10,9 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Topic
import androidx.compose.material3.Checkbox
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
@ -37,6 +20,8 @@ import androidx.compose.ui.unit.dp
import app.revanced.manager.R
import app.revanced.manager.ui.component.AlertDialogExtended
import app.revanced.manager.ui.component.TextHorizontalPadding
import app.revanced.manager.ui.component.haptics.HapticCheckbox
import app.revanced.manager.ui.component.haptics.HapticRadioButton
import app.revanced.manager.ui.model.BundleType
import app.revanced.manager.util.APK_MIMETYPE
import app.revanced.manager.util.JAR_MIMETYPE
@ -45,7 +30,7 @@ import app.revanced.manager.util.JAR_MIMETYPE
fun ImportPatchBundleDialog(
onDismiss: () -> Unit,
onRemoteSubmit: (String, Boolean) -> Unit,
onLocalSubmit: (Uri, Uri?) -> Unit
onLocalSubmit: (Uri, Uri?) -> Unit,
) {
var currentStep by rememberSaveable { mutableIntStateOf(0) }
var bundleType by rememberSaveable { mutableStateOf(BundleType.Remote) }
@ -72,31 +57,32 @@ fun ImportPatchBundleDialog(
integrationsActivityLauncher.launch(APK_MIMETYPE)
}
val steps = listOf<@Composable () -> Unit>(
{
SelectBundleTypeStep(bundleType) { selectedType ->
bundleType = selectedType
}
},
{
ImportBundleStep(
bundleType,
patchBundle,
integrations,
remoteUrl,
autoUpdate,
{ launchPatchActivity() },
{ launchIntegrationsActivity() },
{ remoteUrl = it },
{ autoUpdate = it }
)
}
)
val steps =
listOf<@Composable () -> Unit>(
{
SelectBundleTypeStep(bundleType) { selectedType ->
bundleType = selectedType
}
},
{
ImportBundleStep(
bundleType,
patchBundle,
integrations,
remoteUrl,
autoUpdate,
{ launchPatchActivity() },
{ launchIntegrationsActivity() },
{ remoteUrl = it },
{ autoUpdate = it },
)
},
)
val inputsAreValid by remember {
derivedStateOf {
(bundleType == BundleType.Local && patchBundle != null) ||
(bundleType == BundleType.Remote && remoteUrl.isNotEmpty())
(bundleType == BundleType.Remote && remoteUrl.isNotEmpty())
}
}
@ -114,16 +100,17 @@ fun ImportPatchBundleDialog(
enabled = inputsAreValid,
onClick = {
when (bundleType) {
BundleType.Local -> patchBundle?.let {
onLocalSubmit(
it,
integrations
)
}
BundleType.Local ->
patchBundle?.let {
onLocalSubmit(
it,
integrations,
)
}
BundleType.Remote -> onRemoteSubmit(remoteUrl, autoUpdate)
}
}
},
) {
Text(stringResource(R.string.add))
}
@ -144,53 +131,55 @@ fun ImportPatchBundleDialog(
}
}
},
textHorizontalPadding = PaddingValues(0.dp)
textHorizontalPadding = PaddingValues(0.dp),
)
}
@Composable
fun SelectBundleTypeStep(
bundleType: BundleType,
onBundleTypeSelected: (BundleType) -> Unit
onBundleTypeSelected: (BundleType) -> Unit,
) {
Column(
verticalArrangement = Arrangement.spacedBy(24.dp)
verticalArrangement = Arrangement.spacedBy(24.dp),
) {
Text(
modifier = Modifier.padding(horizontal = 24.dp),
text = stringResource(R.string.select_bundle_type_dialog_description)
text = stringResource(R.string.select_bundle_type_dialog_description),
)
Column {
ListItem(
modifier = Modifier.clickable(
role = Role.RadioButton,
onClick = { onBundleTypeSelected(BundleType.Remote) }
),
modifier =
Modifier.clickable(
role = Role.RadioButton,
onClick = { onBundleTypeSelected(BundleType.Remote) },
),
headlineContent = { Text(stringResource(R.string.enter_url)) },
overlineContent = { Text(stringResource(R.string.recommended)) },
supportingContent = { Text(stringResource(R.string.remote_bundle_description)) },
leadingContent = {
RadioButton(
HapticRadioButton(
selected = bundleType == BundleType.Remote,
onClick = null
onClick = null,
)
}
},
)
HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp))
ListItem(
modifier = Modifier.clickable(
role = Role.RadioButton,
onClick = { onBundleTypeSelected(BundleType.Local) }
),
modifier =
Modifier.clickable(
role = Role.RadioButton,
onClick = { onBundleTypeSelected(BundleType.Local) },
),
headlineContent = { Text(stringResource(R.string.select_from_storage)) },
supportingContent = { Text(stringResource(R.string.local_bundle_description)) },
overlineContent = { },
leadingContent = {
RadioButton(
HapticRadioButton(
selected = bundleType == BundleType.Local,
onClick = null
onClick = null,
)
}
},
)
}
}
@ -207,67 +196,92 @@ fun ImportBundleStep(
launchPatchActivity: () -> Unit,
launchIntegrationsActivity: () -> Unit,
onRemoteUrlChange: (String) -> Unit,
onAutoUpdateChange: (Boolean) -> Unit
onAutoUpdateChange: (Boolean) -> Unit,
) {
Column {
when (bundleType) {
BundleType.Local -> {
Column(
modifier = Modifier.padding(horizontal = 8.dp)
modifier = Modifier.padding(horizontal = 8.dp),
) {
ListItem(
headlineContent = {
Text(stringResource(R.string.patch_bundle_field))
},
supportingContent = { Text(stringResource(if (patchBundle != null) R.string.file_field_set else R.string.file_field_not_set)) },
supportingContent = {
Text(
stringResource(
if (patchBundle !=
null
) {
R.string.file_field_set
} else {
R.string.file_field_not_set
},
),
)
},
trailingContent = {
IconButton(onClick = launchPatchActivity) {
Icon(imageVector = Icons.Default.Topic, contentDescription = null)
}
},
modifier = Modifier.clickable { launchPatchActivity() }
modifier = Modifier.clickable { launchPatchActivity() },
)
ListItem(
headlineContent = {
Text(stringResource(R.string.integrations_field))
},
supportingContent = { Text(stringResource(if (integrations != null) R.string.file_field_set else R.string.file_field_not_set)) },
supportingContent = {
Text(
stringResource(
if (integrations !=
null
) {
R.string.file_field_set
} else {
R.string.file_field_not_set
},
),
)
},
trailingContent = {
IconButton(onClick = launchIntegrationsActivity) {
Icon(imageVector = Icons.Default.Topic, contentDescription = null)
}
},
modifier = Modifier.clickable { launchIntegrationsActivity() }
modifier = Modifier.clickable { launchIntegrationsActivity() },
)
}
}
BundleType.Remote -> {
Column(
modifier = Modifier.padding(TextHorizontalPadding)
modifier = Modifier.padding(TextHorizontalPadding),
) {
OutlinedTextField(
value = remoteUrl,
onValueChange = onRemoteUrlChange,
label = { Text(stringResource(R.string.bundle_url)) }
label = { Text(stringResource(R.string.bundle_url)) },
)
}
Column(
modifier = Modifier.padding(horizontal = 8.dp)
modifier = Modifier.padding(horizontal = 8.dp),
) {
ListItem(
modifier = Modifier.clickable(
role = Role.Checkbox,
onClick = { onAutoUpdateChange(!autoUpdate) }
),
modifier =
Modifier.clickable(
role = Role.Checkbox,
onClick = { onAutoUpdateChange(!autoUpdate) },
),
headlineContent = { Text(stringResource(R.string.auto_update)) },
leadingContent = {
CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) {
Checkbox(
HapticCheckbox(
checked = autoUpdate,
onCheckedChange = {
onAutoUpdateChange(!autoUpdate)
}
},
)
}
},
@ -276,4 +290,4 @@ fun ImportBundleStep(
}
}
}
}
}

View file

@ -1,29 +1,24 @@
package app.revanced.manager.ui.component.haptics
import android.view.HapticFeedbackConstants
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.RowScope
import androidx.compose.material3.RadioButton
import androidx.compose.material3.RadioButtonColors
import androidx.compose.material3.RadioButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalView
@Composable
fun HapticRadioButton (
fun HapticRadioButton(
selected: Boolean,
onClick: (() -> Unit)?,
modifier: Modifier = Modifier,
enabled: Boolean = true,
colors: RadioButtonColors = RadioButtonDefaults.colors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
val selectedState = remember { mutableStateOf(selected) }
@ -42,6 +37,6 @@ fun HapticRadioButton (
modifier = modifier,
enabled = enabled,
colors = colors,
interactionSource = interactionSource
interactionSource = interactionSource,
)
}

View file

@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ListItem
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@ -16,11 +15,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import app.revanced.manager.R
import app.revanced.manager.data.room.apps.installed.InstallType
import app.revanced.manager.ui.component.haptics.HapticRadioButton
@Composable
fun InstallPickerDialog(
onDismiss: () -> Unit,
onConfirm: (InstallType) -> Unit
onConfirm: (InstallType) -> Unit,
) {
var selectedInstallType by rememberSaveable { mutableStateOf(InstallType.DEFAULT) }
@ -36,7 +36,7 @@ fun InstallPickerDialog(
onClick = {
onConfirm(selectedInstallType)
onDismiss()
}
},
) {
Text(stringResource(R.string.install_app))
}
@ -44,19 +44,19 @@ fun InstallPickerDialog(
title = { Text(stringResource(R.string.select_install_type)) },
text = {
Column {
InstallType.values().forEach {
InstallType.entries.forEach {
ListItem(
modifier = Modifier.clickable { selectedInstallType = it },
leadingContent = {
RadioButton(
HapticRadioButton(
selected = selectedInstallType == it,
onClick = null
onClick = null,
)
},
headlineContent = { Text(stringResource(it.stringResource)) }
headlineContent = { Text(stringResource(it.stringResource)) },
)
}
}
}
},
)
}
}

View file

@ -31,13 +31,11 @@ import androidx.compose.material3.AlertDialog
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@ -65,6 +63,8 @@ import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.FloatInputDialog
import app.revanced.manager.ui.component.IntInputDialog
import app.revanced.manager.ui.component.LongInputDialog
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
import app.revanced.manager.ui.component.haptics.HapticRadioButton
import app.revanced.manager.ui.component.haptics.HapticSwitch
import app.revanced.manager.util.isScrollingUp
import app.revanced.manager.util.mutableStateSetOf
@ -443,7 +443,7 @@ private class PresetOptionEditor<T : Any>(
headlineContent = { Text(title) },
supportingContent = value?.toString()?.let { { Text(it) } },
leadingContent = {
RadioButton(
HapticRadioButton(
selected = selectedPreset == presetKey,
onClick = { selectedPreset = presetKey },
)
@ -601,7 +601,7 @@ private class ListOptionEditor<T : Serializable>(
floatingActionButton = {
if (deleteMode) return@Scaffold
ExtendedFloatingActionButton(
HapticExtendedFloatingActionButton(
text = { Text(stringResource(R.string.add)) },
icon = { Icon(Icons.Outlined.Add, null) },
expanded = lazyListState.isScrollingUp,

View file

@ -35,6 +35,7 @@ import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.LazyColumnWithScrollbar
import app.revanced.manager.ui.component.SafeguardDialog
import app.revanced.manager.ui.component.SearchView
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
import app.revanced.manager.ui.component.patches.OptionItem
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel.Companion.SHOW_SUPPORTED
@ -50,15 +51,16 @@ import kotlinx.coroutines.launch
fun PatchesSelectorScreen(
onSave: (PatchSelection?, Options) -> Unit,
onBackClick: () -> Unit,
vm: PatchesSelectorViewModel
vm: PatchesSelectorViewModel,
) {
val bundles by vm.bundlesFlow.collectAsStateWithLifecycle(initialValue = emptyList())
val pagerState = rememberPagerState(
initialPage = 0,
initialPageOffsetFraction = 0f
) {
bundles.size
}
val pagerState =
rememberPagerState(
initialPage = 0,
initialPageOffsetFraction = 0f,
) {
bundles.size
}
val composableScope = rememberCoroutineScope()
var search: String? by rememberSaveable {
mutableStateOf(null)
@ -74,30 +76,30 @@ fun PatchesSelectorScreen(
ModalBottomSheet(
onDismissRequest = {
showBottomSheet = false
}
},
) {
Column(
modifier = Modifier.padding(horizontal = 24.dp)
modifier = Modifier.padding(horizontal = 24.dp),
) {
Text(
text = stringResource(R.string.patch_selector_sheet_filter_title),
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.padding(bottom = 16.dp)
modifier = Modifier.padding(bottom = 16.dp),
)
Text(
text = stringResource(R.string.patch_selector_sheet_filter_compat_title),
style = MaterialTheme.typography.titleMedium
style = MaterialTheme.typography.titleMedium,
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(5.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)) }
label = { Text(stringResource(R.string.supported)) },
)
FilterChip(
@ -116,12 +118,13 @@ fun PatchesSelectorScreen(
}
}
if (vm.compatibleVersions.isNotEmpty())
if (vm.compatibleVersions.isNotEmpty()) {
UnsupportedDialog(
appVersion = vm.appVersion,
supportedVersions = vm.compatibleVersions,
onDismissRequest = vm::dismissDialogs
onDismissRequest = vm::dismissDialogs,
)
}
vm.optionsDialog?.let { (bundle, patch) ->
OptionsDialog(
@ -129,7 +132,7 @@ fun PatchesSelectorScreen(
patch = patch,
values = vm.getOptions(bundle, patch),
reset = { vm.resetOptions(bundle, patch) },
set = { key, value -> vm.setOption(bundle, patch, key, value) }
set = { key, value -> vm.setOption(bundle, patch, key, value) },
)
}
@ -142,7 +145,7 @@ fun PatchesSelectorScreen(
vm.pendingUniversalPatchAction?.let {
UniversalPatchWarningDialog(
onCancel = vm::dismissUniversalPatchWarning,
onConfirm = vm::confirmUniversalPatchWarning
onConfirm = vm::confirmUniversalPatchWarning,
)
}
@ -151,7 +154,7 @@ fun PatchesSelectorScreen(
patches: List<PatchInfo>,
filterFlag: Int,
supported: Boolean,
header: (@Composable () -> Unit)? = null
header: (@Composable () -> Unit)? = null,
) {
if (patches.isNotEmpty() && (vm.filter and filterFlag) != 0 || vm.filter == 0) {
header?.let {
@ -162,17 +165,19 @@ fun PatchesSelectorScreen(
items(
items = patches,
key = { it.name }
key = { it.name },
) { patch ->
PatchItem(
patch = patch,
onOptionsDialog = {
vm.optionsDialog = uid to patch
},
selected = supported && vm.isSelected(
uid,
patch
),
selected =
supported &&
vm.isSelected(
uid,
patch,
),
onToggle = {
if (vm.selectionWarningEnabled) {
showSelectionWarning = true
@ -182,7 +187,7 @@ fun PatchesSelectorScreen(
vm.togglePatch(uid, patch)
}
},
supported = supported
supported = supported,
)
}
}
@ -193,28 +198,29 @@ fun PatchesSelectorScreen(
query = query,
onQueryChange = { search = it },
onActiveChange = { if (!it) search = null },
placeholder = { Text(stringResource(R.string.search_patches)) }
placeholder = { Text(stringResource(R.string.search_patches)) },
) {
val bundle = bundles[pagerState.currentPage]
LazyColumnWithScrollbar(
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxSize(),
) {
fun List<PatchInfo>.searched() = filter {
it.name.contains(query, true)
}
fun List<PatchInfo>.searched() =
filter {
it.name.contains(query, true)
}
patchList(
uid = bundle.uid,
patches = bundle.supported.searched(),
filterFlag = SHOW_SUPPORTED,
supported = true
supported = true,
)
patchList(
uid = bundle.uid,
patches = bundle.universal.searched(),
filterFlag = SHOW_UNIVERSAL,
supported = true
supported = true,
) {
ListHeader(
title = stringResource(R.string.universal_patches),
@ -226,11 +232,11 @@ fun PatchesSelectorScreen(
uid = bundle.uid,
patches = bundle.unsupported.searched(),
filterFlag = SHOW_UNSUPPORTED,
supported = true
supported = true,
) {
ListHeader(
title = stringResource(R.string.unsupported_patches),
onHelpClick = { vm.openUnsupportedDialog(bundle.unsupported) }
onHelpClick = { vm.openUnsupportedDialog(bundle.unsupported) },
)
}
}
@ -252,11 +258,11 @@ fun PatchesSelectorScreen(
IconButton(
onClick = {
search = ""
}
},
) {
Icon(Icons.Outlined.Search, stringResource(R.string.search))
}
}
},
)
},
floatingActionButton = {
@ -269,19 +275,19 @@ fun PatchesSelectorScreen(
onClick = {
// TODO: only allow this if all required options have been set.
onSave(vm.getCustomSelection(), vm.getOptions())
}
},
)
}
},
) { paddingValues ->
Column(
Modifier
.fillMaxSize()
.padding(paddingValues)
.padding(paddingValues),
) {
if (bundles.size > 1) {
ScrollableTabRow(
selectedTabIndex = pagerState.currentPage,
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp)
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp),
) {
bundles.forEachIndexed { index, bundle ->
HapticTab(
@ -289,13 +295,13 @@ fun PatchesSelectorScreen(
onClick = {
composableScope.launch {
pagerState.animateScrollToPage(
index
index,
)
}
},
text = { Text(bundle.name) },
selectedContentColor = MaterialTheme.colorScheme.primary,
unselectedContentColor = MaterialTheme.colorScheme.onSurfaceVariant
unselectedContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
@ -311,19 +317,19 @@ fun PatchesSelectorScreen(
LazyColumnWithScrollbar(
modifier = Modifier.fillMaxSize(),
state = patchLazyListStates[index]
state = patchLazyListStates[index],
) {
patchList(
uid = bundle.uid,
patches = bundle.supported,
filterFlag = SHOW_SUPPORTED,
supported = true
supported = true,
)
patchList(
uid = bundle.uid,
patches = bundle.universal,
filterFlag = SHOW_UNIVERSAL,
supported = true
supported = true,
) {
ListHeader(
title = stringResource(R.string.universal_patches),
@ -333,15 +339,15 @@ fun PatchesSelectorScreen(
uid = bundle.uid,
patches = bundle.unsupported,
filterFlag = SHOW_UNSUPPORTED,
supported = vm.allowIncompatiblePatches
supported = vm.allowIncompatiblePatches,
) {
ListHeader(
title = stringResource(R.string.unsupported_patches),
onHelpClick = { vm.openUnsupportedDialog(bundle.unsupported) }
onHelpClick = { vm.openUnsupportedDialog(bundle.unsupported) },
)
}
}
}
},
)
}
}
@ -359,7 +365,7 @@ fun SelectionWarningDialog(onDismiss: () -> Unit) {
@Composable
fun UniversalPatchWarningDialog(
onCancel: () -> Unit,
onConfirm: () -> Unit
onConfirm: () -> Unit,
) {
AlertDialog(
onDismissRequest = onCancel,
@ -379,12 +385,12 @@ fun UniversalPatchWarningDialog(
title = {
Text(
text = stringResource(R.string.warning),
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center)
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center),
)
},
text = {
Text(stringResource(R.string.universal_patch_warning_description))
}
},
)
}
@ -394,17 +400,18 @@ fun PatchItem(
onOptionsDialog: () -> Unit,
selected: Boolean,
onToggle: () -> Unit,
supported: Boolean = true
) = ListItem (
modifier = Modifier
.let { if (!supported) it.alpha(0.5f) else it }
.clickable(enabled = supported, onClick = onToggle)
.fillMaxSize(),
supported: Boolean = true,
) = ListItem(
modifier =
Modifier
.let { if (!supported) it.alpha(0.5f) else it }
.clickable(enabled = supported, onClick = onToggle)
.fillMaxSize(),
leadingContent = {
HapticCheckbox(
checked = selected,
onCheckedChange = { onToggle() },
enabled = supported
enabled = supported,
)
},
headlineContent = { Text(patch.name) },
@ -421,26 +428,27 @@ fun PatchItem(
@Composable
fun ListHeader(
title: String,
onHelpClick: (() -> Unit)? = null
onHelpClick: (() -> Unit)? = null,
) {
ListItem(
headlineContent = {
Text(
text = title,
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.labelLarge
style = MaterialTheme.typography.labelLarge,
)
},
trailingContent = onHelpClick?.let {
{
IconButton(onClick = it) {
Icon(
Icons.AutoMirrored.Outlined.HelpOutline,
stringResource(R.string.help)
)
trailingContent =
onHelpClick?.let {
{
IconButton(onClick = it) {
Icon(
Icons.AutoMirrored.Outlined.HelpOutline,
stringResource(R.string.help),
)
}
}
}
}
},
)
}
@ -448,7 +456,7 @@ fun ListHeader(
fun UnsupportedDialog(
appVersion: String,
supportedVersions: List<String>,
onDismissRequest: () -> Unit
onDismissRequest: () -> Unit,
) = AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
@ -462,10 +470,10 @@ fun UnsupportedDialog(
stringResource(
R.string.app_not_supported,
appVersion,
supportedVersions.joinToString(", ")
)
supportedVersions.joinToString(", "),
),
)
}
},
)
@OptIn(ExperimentalMaterial3Api::class)
@ -478,10 +486,11 @@ fun OptionsDialog(
onDismissRequest: () -> Unit,
) = Dialog(
onDismissRequest = onDismissRequest,
properties = DialogProperties(
usePlatformDefaultWidth = false,
dismissOnBackPress = true
)
properties =
DialogProperties(
usePlatformDefaultWidth = false,
dismissOnBackPress = true,
),
) {
Scaffold(
topBar = {
@ -492,12 +501,12 @@ fun OptionsDialog(
IconButton(onClick = reset) {
Icon(Icons.Outlined.Restore, stringResource(R.string.reset))
}
}
},
)
}
},
) { paddingValues ->
LazyColumnWithScrollbar(
modifier = Modifier.padding(paddingValues)
modifier = Modifier.padding(paddingValues),
) {
if (patch.options == null) return@LazyColumnWithScrollbar
@ -511,4 +520,4 @@ fun OptionsDialog(
}
}
}
}
}

View file

@ -9,7 +9,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowRight
import androidx.compose.material.icons.filled.AutoFixHigh
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
@ -28,6 +27,7 @@ import app.revanced.manager.R
import app.revanced.manager.ui.component.AppInfo
import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.ColumnWithScrollbar
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
import app.revanced.manager.ui.destination.SelectedAppInfoDestination
import app.revanced.manager.ui.model.BundleInfo.Extensions.bundleInfoFlow
import app.revanced.manager.ui.model.SelectedApp
@ -48,7 +48,7 @@ import org.koin.core.parameter.parametersOf
fun SelectedAppInfoScreen(
onPatchClick: (SelectedApp, PatchSelection, Options) -> Unit,
onBackClick: () -> Unit,
vm: SelectedAppInfoViewModel
vm: SelectedAppInfoViewModel,
) {
val context = LocalContext.current
@ -82,67 +82,71 @@ fun SelectedAppInfoScreen(
AnimatedNavHost(controller = navController) { destination ->
when (destination) {
is SelectedAppInfoDestination.Main -> SelectedAppInfoScreen(
onPatchClick = patchClick@{
if (selectedPatchCount == 0) {
context.toast(context.getString(R.string.no_patches_selected))
is SelectedAppInfoDestination.Main ->
SelectedAppInfoScreen(
onPatchClick = patchClick@{
if (selectedPatchCount == 0) {
context.toast(context.getString(R.string.no_patches_selected))
return@patchClick
}
onPatchClick(
vm.selectedApp,
patches,
vm.getOptionsFiltered(bundles)
)
},
onPatchSelectorClick = {
navController.navigate(
SelectedAppInfoDestination.PatchesSelector(
return@patchClick
}
onPatchClick(
vm.selectedApp,
vm.getCustomPatches(
bundles,
allowIncompatiblePatches
patches,
vm.getOptionsFiltered(bundles),
)
},
onPatchSelectorClick = {
navController.navigate(
SelectedAppInfoDestination.PatchesSelector(
vm.selectedApp,
vm.getCustomPatches(
bundles,
allowIncompatiblePatches,
),
vm.options,
),
vm.options
)
)
},
onVersionSelectorClick = {
navController.navigate(SelectedAppInfoDestination.VersionSelector)
},
onBackClick = onBackClick,
availablePatchCount = availablePatchCount,
selectedPatchCount = selectedPatchCount,
packageName = packageName,
version = version,
packageInfo = vm.selectedAppInfo,
)
},
onVersionSelectorClick = {
navController.navigate(SelectedAppInfoDestination.VersionSelector)
},
onBackClick = onBackClick,
availablePatchCount = availablePatchCount,
selectedPatchCount = selectedPatchCount,
packageName = packageName,
version = version,
packageInfo = vm.selectedAppInfo,
)
is SelectedAppInfoDestination.VersionSelector -> VersionSelectorScreen(
onBackClick = navController::pop,
onAppClick = {
vm.selectedApp = it
navController.pop()
},
viewModel = koinViewModel { parametersOf(packageName) }
)
is SelectedAppInfoDestination.VersionSelector ->
VersionSelectorScreen(
onBackClick = navController::pop,
onAppClick = {
vm.selectedApp = it
navController.pop()
},
viewModel = koinViewModel { parametersOf(packageName) },
)
is SelectedAppInfoDestination.PatchesSelector -> PatchesSelectorScreen(
onSave = { patches, options ->
vm.updateConfiguration(patches, options, bundles)
navController.pop()
},
onBackClick = navController::pop,
vm = koinViewModel {
parametersOf(
PatchesSelectorViewModel.Params(
destination.app,
destination.currentSelection,
destination.options,
)
)
}
)
is SelectedAppInfoDestination.PatchesSelector ->
PatchesSelectorScreen(
onSave = { patches, options ->
vm.updateConfiguration(patches, options, bundles)
navController.pop()
},
onBackClick = navController::pop,
vm =
koinViewModel {
parametersOf(
PatchesSelectorViewModel.Params(
destination.app,
destination.currentSelection,
destination.options,
),
)
},
)
}
}
}
@ -164,21 +168,22 @@ private fun SelectedAppInfoScreen(
topBar = {
AppTopBar(
title = stringResource(R.string.app_info),
onBackClick = onBackClick
onBackClick = onBackClick,
)
},
floatingActionButton = {
ExtendedFloatingActionButton(
HapticExtendedFloatingActionButton(
text = { Text(stringResource(R.string.patch)) },
icon = { Icon(Icons.Default.AutoFixHigh, null) },
onClick = onPatchClick
onClick = onPatchClick,
)
}
},
) { paddingValues ->
ColumnWithScrollbar(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
modifier =
Modifier
.fillMaxSize()
.padding(paddingValues),
) {
AppInfo(packageInfo, placeholderLabel = packageName) {
Text(
@ -191,39 +196,44 @@ private fun SelectedAppInfoScreen(
PageItem(
R.string.patch_selector_item,
stringResource(R.string.patch_selector_item_description, selectedPatchCount),
onPatchSelectorClick
onPatchSelectorClick,
)
PageItem(
R.string.version_selector_item,
stringResource(R.string.version_selector_item_description, version),
onVersionSelectorClick
onVersionSelectorClick,
)
}
}
}
@Composable
private fun PageItem(@StringRes title: Int, description: String, onClick: () -> Unit) {
private fun PageItem(
@StringRes title: Int,
description: String,
onClick: () -> Unit,
) {
ListItem(
modifier = Modifier
.clickable(onClick = onClick)
.padding(start = 8.dp),
modifier =
Modifier
.clickable(onClick = onClick)
.padding(start = 8.dp),
headlineContent = {
Text(
stringResource(title),
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.titleLarge
style = MaterialTheme.typography.titleLarge,
)
},
supportingContent = {
Text(
description,
color = MaterialTheme.colorScheme.outline,
style = MaterialTheme.typography.bodyMedium
style = MaterialTheme.typography.bodyMedium,
)
},
trailingContent = {
Icon(Icons.AutoMirrored.Outlined.ArrowRight, null)
}
},
)
}
}

View file

@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Checkbox
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@ -20,6 +19,7 @@ import app.revanced.manager.R
import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.ColumnWithScrollbar
import app.revanced.manager.ui.component.GroupHeader
import app.revanced.manager.ui.component.haptics.HapticCheckbox
import app.revanced.manager.ui.component.settings.BooleanItem
import app.revanced.manager.ui.component.settings.SettingsListItem
import app.revanced.manager.ui.viewmodel.DownloadsViewModel
@ -29,7 +29,7 @@ import org.koin.androidx.compose.koinViewModel
@Composable
fun DownloadsSettingsScreen(
onBackClick: () -> Unit,
viewModel: DownloadsViewModel = koinViewModel()
viewModel: DownloadsViewModel = koinViewModel(),
) {
val prefs = viewModel.prefs
@ -46,14 +46,15 @@ fun DownloadsSettingsScreen(
Icon(Icons.Default.Delete, stringResource(R.string.delete))
}
}
}
},
)
}
},
) { paddingValues ->
ColumnWithScrollbar(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
modifier =
Modifier
.fillMaxSize()
.padding(paddingValues),
) {
BooleanItem(
preference = prefs.preferSplits,
@ -69,16 +70,21 @@ fun DownloadsSettingsScreen(
SettingsListItem(
modifier = Modifier.clickable { viewModel.toggleItem(app) },
headlineContent = app.packageName,
leadingContent = (@Composable {
Checkbox(
checked = selected,
onCheckedChange = { viewModel.toggleItem(app) }
)
}).takeIf { viewModel.selection.isNotEmpty() },
leadingContent =
{
(
@Composable {
HapticCheckbox(
checked = selected,
onCheckedChange = { viewModel.toggleItem(app) },
)
}
).takeIf { viewModel.selection.isNotEmpty() }
},
supportingContent = app.version,
tonalElevation = if (selected) 8.dp else 0.dp
tonalElevation = if (selected) 8.dp else 0.dp,
)
}
}
}
}
}