mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2024-11-10 01:01:56 +01:00
feat: Migrate more buttons
This commit is contained in:
parent
bfbf27024d
commit
968c665791
7 changed files with 306 additions and 272 deletions
|
@ -10,26 +10,9 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Topic
|
import androidx.compose.material.icons.filled.Topic
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.runtime.*
|
||||||
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.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.Role
|
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.R
|
||||||
import app.revanced.manager.ui.component.AlertDialogExtended
|
import app.revanced.manager.ui.component.AlertDialogExtended
|
||||||
import app.revanced.manager.ui.component.TextHorizontalPadding
|
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.ui.model.BundleType
|
||||||
import app.revanced.manager.util.APK_MIMETYPE
|
import app.revanced.manager.util.APK_MIMETYPE
|
||||||
import app.revanced.manager.util.JAR_MIMETYPE
|
import app.revanced.manager.util.JAR_MIMETYPE
|
||||||
|
@ -45,7 +30,7 @@ import app.revanced.manager.util.JAR_MIMETYPE
|
||||||
fun ImportPatchBundleDialog(
|
fun ImportPatchBundleDialog(
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onRemoteSubmit: (String, Boolean) -> Unit,
|
onRemoteSubmit: (String, Boolean) -> Unit,
|
||||||
onLocalSubmit: (Uri, Uri?) -> Unit
|
onLocalSubmit: (Uri, Uri?) -> Unit,
|
||||||
) {
|
) {
|
||||||
var currentStep by rememberSaveable { mutableIntStateOf(0) }
|
var currentStep by rememberSaveable { mutableIntStateOf(0) }
|
||||||
var bundleType by rememberSaveable { mutableStateOf(BundleType.Remote) }
|
var bundleType by rememberSaveable { mutableStateOf(BundleType.Remote) }
|
||||||
|
@ -72,31 +57,32 @@ fun ImportPatchBundleDialog(
|
||||||
integrationsActivityLauncher.launch(APK_MIMETYPE)
|
integrationsActivityLauncher.launch(APK_MIMETYPE)
|
||||||
}
|
}
|
||||||
|
|
||||||
val steps = listOf<@Composable () -> Unit>(
|
val steps =
|
||||||
{
|
listOf<@Composable () -> Unit>(
|
||||||
SelectBundleTypeStep(bundleType) { selectedType ->
|
{
|
||||||
bundleType = selectedType
|
SelectBundleTypeStep(bundleType) { selectedType ->
|
||||||
}
|
bundleType = selectedType
|
||||||
},
|
}
|
||||||
{
|
},
|
||||||
ImportBundleStep(
|
{
|
||||||
bundleType,
|
ImportBundleStep(
|
||||||
patchBundle,
|
bundleType,
|
||||||
integrations,
|
patchBundle,
|
||||||
remoteUrl,
|
integrations,
|
||||||
autoUpdate,
|
remoteUrl,
|
||||||
{ launchPatchActivity() },
|
autoUpdate,
|
||||||
{ launchIntegrationsActivity() },
|
{ launchPatchActivity() },
|
||||||
{ remoteUrl = it },
|
{ launchIntegrationsActivity() },
|
||||||
{ autoUpdate = it }
|
{ remoteUrl = it },
|
||||||
)
|
{ autoUpdate = it },
|
||||||
}
|
)
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
|
||||||
val inputsAreValid by remember {
|
val inputsAreValid by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
(bundleType == BundleType.Local && patchBundle != null) ||
|
(bundleType == BundleType.Local && patchBundle != null) ||
|
||||||
(bundleType == BundleType.Remote && remoteUrl.isNotEmpty())
|
(bundleType == BundleType.Remote && remoteUrl.isNotEmpty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,16 +100,17 @@ fun ImportPatchBundleDialog(
|
||||||
enabled = inputsAreValid,
|
enabled = inputsAreValid,
|
||||||
onClick = {
|
onClick = {
|
||||||
when (bundleType) {
|
when (bundleType) {
|
||||||
BundleType.Local -> patchBundle?.let {
|
BundleType.Local ->
|
||||||
onLocalSubmit(
|
patchBundle?.let {
|
||||||
it,
|
onLocalSubmit(
|
||||||
integrations
|
it,
|
||||||
)
|
integrations,
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BundleType.Remote -> onRemoteSubmit(remoteUrl, autoUpdate)
|
BundleType.Remote -> onRemoteSubmit(remoteUrl, autoUpdate)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.add))
|
Text(stringResource(R.string.add))
|
||||||
}
|
}
|
||||||
|
@ -144,53 +131,55 @@ fun ImportPatchBundleDialog(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
textHorizontalPadding = PaddingValues(0.dp)
|
textHorizontalPadding = PaddingValues(0.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SelectBundleTypeStep(
|
fun SelectBundleTypeStep(
|
||||||
bundleType: BundleType,
|
bundleType: BundleType,
|
||||||
onBundleTypeSelected: (BundleType) -> Unit
|
onBundleTypeSelected: (BundleType) -> Unit,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(24.dp)
|
verticalArrangement = Arrangement.spacedBy(24.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(horizontal = 24.dp),
|
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 {
|
Column {
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier.clickable(
|
modifier =
|
||||||
role = Role.RadioButton,
|
Modifier.clickable(
|
||||||
onClick = { onBundleTypeSelected(BundleType.Remote) }
|
role = Role.RadioButton,
|
||||||
),
|
onClick = { onBundleTypeSelected(BundleType.Remote) },
|
||||||
|
),
|
||||||
headlineContent = { Text(stringResource(R.string.enter_url)) },
|
headlineContent = { Text(stringResource(R.string.enter_url)) },
|
||||||
overlineContent = { Text(stringResource(R.string.recommended)) },
|
overlineContent = { Text(stringResource(R.string.recommended)) },
|
||||||
supportingContent = { Text(stringResource(R.string.remote_bundle_description)) },
|
supportingContent = { Text(stringResource(R.string.remote_bundle_description)) },
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
RadioButton(
|
HapticRadioButton(
|
||||||
selected = bundleType == BundleType.Remote,
|
selected = bundleType == BundleType.Remote,
|
||||||
onClick = null
|
onClick = null,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp))
|
HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp))
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier.clickable(
|
modifier =
|
||||||
role = Role.RadioButton,
|
Modifier.clickable(
|
||||||
onClick = { onBundleTypeSelected(BundleType.Local) }
|
role = Role.RadioButton,
|
||||||
),
|
onClick = { onBundleTypeSelected(BundleType.Local) },
|
||||||
|
),
|
||||||
headlineContent = { Text(stringResource(R.string.select_from_storage)) },
|
headlineContent = { Text(stringResource(R.string.select_from_storage)) },
|
||||||
supportingContent = { Text(stringResource(R.string.local_bundle_description)) },
|
supportingContent = { Text(stringResource(R.string.local_bundle_description)) },
|
||||||
overlineContent = { },
|
overlineContent = { },
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
RadioButton(
|
HapticRadioButton(
|
||||||
selected = bundleType == BundleType.Local,
|
selected = bundleType == BundleType.Local,
|
||||||
onClick = null
|
onClick = null,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,67 +196,92 @@ fun ImportBundleStep(
|
||||||
launchPatchActivity: () -> Unit,
|
launchPatchActivity: () -> Unit,
|
||||||
launchIntegrationsActivity: () -> Unit,
|
launchIntegrationsActivity: () -> Unit,
|
||||||
onRemoteUrlChange: (String) -> Unit,
|
onRemoteUrlChange: (String) -> Unit,
|
||||||
onAutoUpdateChange: (Boolean) -> Unit
|
onAutoUpdateChange: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
when (bundleType) {
|
when (bundleType) {
|
||||||
BundleType.Local -> {
|
BundleType.Local -> {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(horizontal = 8.dp)
|
modifier = Modifier.padding(horizontal = 8.dp),
|
||||||
) {
|
) {
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text(stringResource(R.string.patch_bundle_field))
|
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 = {
|
trailingContent = {
|
||||||
IconButton(onClick = launchPatchActivity) {
|
IconButton(onClick = launchPatchActivity) {
|
||||||
Icon(imageVector = Icons.Default.Topic, contentDescription = null)
|
Icon(imageVector = Icons.Default.Topic, contentDescription = null)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.clickable { launchPatchActivity() }
|
modifier = Modifier.clickable { launchPatchActivity() },
|
||||||
)
|
)
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text(stringResource(R.string.integrations_field))
|
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 = {
|
trailingContent = {
|
||||||
IconButton(onClick = launchIntegrationsActivity) {
|
IconButton(onClick = launchIntegrationsActivity) {
|
||||||
Icon(imageVector = Icons.Default.Topic, contentDescription = null)
|
Icon(imageVector = Icons.Default.Topic, contentDescription = null)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.clickable { launchIntegrationsActivity() }
|
modifier = Modifier.clickable { launchIntegrationsActivity() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BundleType.Remote -> {
|
BundleType.Remote -> {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(TextHorizontalPadding)
|
modifier = Modifier.padding(TextHorizontalPadding),
|
||||||
) {
|
) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = remoteUrl,
|
value = remoteUrl,
|
||||||
onValueChange = onRemoteUrlChange,
|
onValueChange = onRemoteUrlChange,
|
||||||
label = { Text(stringResource(R.string.bundle_url)) }
|
label = { Text(stringResource(R.string.bundle_url)) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(horizontal = 8.dp)
|
modifier = Modifier.padding(horizontal = 8.dp),
|
||||||
) {
|
) {
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier.clickable(
|
modifier =
|
||||||
role = Role.Checkbox,
|
Modifier.clickable(
|
||||||
onClick = { onAutoUpdateChange(!autoUpdate) }
|
role = Role.Checkbox,
|
||||||
),
|
onClick = { onAutoUpdateChange(!autoUpdate) },
|
||||||
|
),
|
||||||
headlineContent = { Text(stringResource(R.string.auto_update)) },
|
headlineContent = { Text(stringResource(R.string.auto_update)) },
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) {
|
CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) {
|
||||||
Checkbox(
|
HapticCheckbox(
|
||||||
checked = autoUpdate,
|
checked = autoUpdate,
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
onAutoUpdateChange(!autoUpdate)
|
onAutoUpdateChange(!autoUpdate)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -276,4 +290,4 @@ fun ImportBundleStep(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,24 @@
|
||||||
package app.revanced.manager.ui.component.haptics
|
package app.revanced.manager.ui.component.haptics
|
||||||
|
|
||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
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.RadioButton
|
||||||
import androidx.compose.material3.RadioButtonColors
|
import androidx.compose.material3.RadioButtonColors
|
||||||
import androidx.compose.material3.RadioButtonDefaults
|
import androidx.compose.material3.RadioButtonDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HapticRadioButton (
|
fun HapticRadioButton(
|
||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
onClick: (() -> Unit)?,
|
onClick: (() -> Unit)?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
colors: RadioButtonColors = RadioButtonDefaults.colors(),
|
colors: RadioButtonColors = RadioButtonDefaults.colors(),
|
||||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
) {
|
) {
|
||||||
val selectedState = remember { mutableStateOf(selected) }
|
val selectedState = remember { mutableStateOf(selected) }
|
||||||
|
|
||||||
|
@ -42,6 +37,6 @@ fun HapticRadioButton (
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
colors = colors,
|
colors = colors,
|
||||||
interactionSource = interactionSource
|
interactionSource = interactionSource,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.RadioButton
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
@ -16,11 +15,12 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.data.room.apps.installed.InstallType
|
import app.revanced.manager.data.room.apps.installed.InstallType
|
||||||
|
import app.revanced.manager.ui.component.haptics.HapticRadioButton
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InstallPickerDialog(
|
fun InstallPickerDialog(
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onConfirm: (InstallType) -> Unit
|
onConfirm: (InstallType) -> Unit,
|
||||||
) {
|
) {
|
||||||
var selectedInstallType by rememberSaveable { mutableStateOf(InstallType.DEFAULT) }
|
var selectedInstallType by rememberSaveable { mutableStateOf(InstallType.DEFAULT) }
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ fun InstallPickerDialog(
|
||||||
onClick = {
|
onClick = {
|
||||||
onConfirm(selectedInstallType)
|
onConfirm(selectedInstallType)
|
||||||
onDismiss()
|
onDismiss()
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.install_app))
|
Text(stringResource(R.string.install_app))
|
||||||
}
|
}
|
||||||
|
@ -44,19 +44,19 @@ fun InstallPickerDialog(
|
||||||
title = { Text(stringResource(R.string.select_install_type)) },
|
title = { Text(stringResource(R.string.select_install_type)) },
|
||||||
text = {
|
text = {
|
||||||
Column {
|
Column {
|
||||||
InstallType.values().forEach {
|
InstallType.entries.forEach {
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier.clickable { selectedInstallType = it },
|
modifier = Modifier.clickable { selectedInstallType = it },
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
RadioButton(
|
HapticRadioButton(
|
||||||
selected = selectedInstallType == it,
|
selected = selectedInstallType == it,
|
||||||
onClick = null
|
onClick = null,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
headlineContent = { Text(stringResource(it.stringResource)) }
|
headlineContent = { Text(stringResource(it.stringResource)) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,13 +31,11 @@ import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
|
||||||
import androidx.compose.material3.Icon
|
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.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.RadioButton
|
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
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.FloatInputDialog
|
||||||
import app.revanced.manager.ui.component.IntInputDialog
|
import app.revanced.manager.ui.component.IntInputDialog
|
||||||
import app.revanced.manager.ui.component.LongInputDialog
|
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.ui.component.haptics.HapticSwitch
|
||||||
import app.revanced.manager.util.isScrollingUp
|
import app.revanced.manager.util.isScrollingUp
|
||||||
import app.revanced.manager.util.mutableStateSetOf
|
import app.revanced.manager.util.mutableStateSetOf
|
||||||
|
@ -443,7 +443,7 @@ private class PresetOptionEditor<T : Any>(
|
||||||
headlineContent = { Text(title) },
|
headlineContent = { Text(title) },
|
||||||
supportingContent = value?.toString()?.let { { Text(it) } },
|
supportingContent = value?.toString()?.let { { Text(it) } },
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
RadioButton(
|
HapticRadioButton(
|
||||||
selected = selectedPreset == presetKey,
|
selected = selectedPreset == presetKey,
|
||||||
onClick = { selectedPreset = presetKey },
|
onClick = { selectedPreset = presetKey },
|
||||||
)
|
)
|
||||||
|
@ -601,7 +601,7 @@ private class ListOptionEditor<T : Serializable>(
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
if (deleteMode) return@Scaffold
|
if (deleteMode) return@Scaffold
|
||||||
|
|
||||||
ExtendedFloatingActionButton(
|
HapticExtendedFloatingActionButton(
|
||||||
text = { Text(stringResource(R.string.add)) },
|
text = { Text(stringResource(R.string.add)) },
|
||||||
icon = { Icon(Icons.Outlined.Add, null) },
|
icon = { Icon(Icons.Outlined.Add, null) },
|
||||||
expanded = lazyListState.isScrollingUp,
|
expanded = lazyListState.isScrollingUp,
|
||||||
|
|
|
@ -35,6 +35,7 @@ import app.revanced.manager.ui.component.AppTopBar
|
||||||
import app.revanced.manager.ui.component.LazyColumnWithScrollbar
|
import app.revanced.manager.ui.component.LazyColumnWithScrollbar
|
||||||
import app.revanced.manager.ui.component.SafeguardDialog
|
import app.revanced.manager.ui.component.SafeguardDialog
|
||||||
import app.revanced.manager.ui.component.SearchView
|
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.component.patches.OptionItem
|
||||||
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_SUPPORTED
|
||||||
|
@ -50,15 +51,16 @@ import kotlinx.coroutines.launch
|
||||||
fun PatchesSelectorScreen(
|
fun PatchesSelectorScreen(
|
||||||
onSave: (PatchSelection?, Options) -> Unit,
|
onSave: (PatchSelection?, Options) -> Unit,
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
vm: PatchesSelectorViewModel
|
vm: PatchesSelectorViewModel,
|
||||||
) {
|
) {
|
||||||
val bundles by vm.bundlesFlow.collectAsStateWithLifecycle(initialValue = emptyList())
|
val bundles by vm.bundlesFlow.collectAsStateWithLifecycle(initialValue = emptyList())
|
||||||
val pagerState = rememberPagerState(
|
val pagerState =
|
||||||
initialPage = 0,
|
rememberPagerState(
|
||||||
initialPageOffsetFraction = 0f
|
initialPage = 0,
|
||||||
) {
|
initialPageOffsetFraction = 0f,
|
||||||
bundles.size
|
) {
|
||||||
}
|
bundles.size
|
||||||
|
}
|
||||||
val composableScope = rememberCoroutineScope()
|
val composableScope = rememberCoroutineScope()
|
||||||
var search: String? by rememberSaveable {
|
var search: String? by rememberSaveable {
|
||||||
mutableStateOf(null)
|
mutableStateOf(null)
|
||||||
|
@ -74,30 +76,30 @@ fun PatchesSelectorScreen(
|
||||||
ModalBottomSheet(
|
ModalBottomSheet(
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
showBottomSheet = false
|
showBottomSheet = false
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(horizontal = 24.dp)
|
modifier = Modifier.padding(horizontal = 24.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.patch_selector_sheet_filter_title),
|
text = stringResource(R.string.patch_selector_sheet_filter_title),
|
||||||
style = MaterialTheme.typography.headlineSmall,
|
style = MaterialTheme.typography.headlineSmall,
|
||||||
modifier = Modifier.padding(bottom = 16.dp)
|
modifier = Modifier.padding(bottom = 16.dp),
|
||||||
)
|
)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.patch_selector_sheet_filter_compat_title),
|
text = stringResource(R.string.patch_selector_sheet_filter_compat_title),
|
||||||
style = MaterialTheme.typography.titleMedium
|
style = MaterialTheme.typography.titleMedium,
|
||||||
)
|
)
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.spacedBy(5.dp)
|
horizontalArrangement = Arrangement.spacedBy(5.dp),
|
||||||
) {
|
) {
|
||||||
FilterChip(
|
FilterChip(
|
||||||
selected = vm.filter and SHOW_SUPPORTED != 0,
|
selected = vm.filter and SHOW_SUPPORTED != 0,
|
||||||
onClick = { vm.toggleFlag(SHOW_SUPPORTED) },
|
onClick = { vm.toggleFlag(SHOW_SUPPORTED) },
|
||||||
label = { Text(stringResource(R.string.supported)) }
|
label = { Text(stringResource(R.string.supported)) },
|
||||||
)
|
)
|
||||||
|
|
||||||
FilterChip(
|
FilterChip(
|
||||||
|
@ -116,12 +118,13 @@ fun PatchesSelectorScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm.compatibleVersions.isNotEmpty())
|
if (vm.compatibleVersions.isNotEmpty()) {
|
||||||
UnsupportedDialog(
|
UnsupportedDialog(
|
||||||
appVersion = vm.appVersion,
|
appVersion = vm.appVersion,
|
||||||
supportedVersions = vm.compatibleVersions,
|
supportedVersions = vm.compatibleVersions,
|
||||||
onDismissRequest = vm::dismissDialogs
|
onDismissRequest = vm::dismissDialogs,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
vm.optionsDialog?.let { (bundle, patch) ->
|
vm.optionsDialog?.let { (bundle, patch) ->
|
||||||
OptionsDialog(
|
OptionsDialog(
|
||||||
|
@ -129,7 +132,7 @@ fun PatchesSelectorScreen(
|
||||||
patch = patch,
|
patch = patch,
|
||||||
values = vm.getOptions(bundle, patch),
|
values = vm.getOptions(bundle, patch),
|
||||||
reset = { vm.resetOptions(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 {
|
vm.pendingUniversalPatchAction?.let {
|
||||||
UniversalPatchWarningDialog(
|
UniversalPatchWarningDialog(
|
||||||
onCancel = vm::dismissUniversalPatchWarning,
|
onCancel = vm::dismissUniversalPatchWarning,
|
||||||
onConfirm = vm::confirmUniversalPatchWarning
|
onConfirm = vm::confirmUniversalPatchWarning,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +154,7 @@ fun PatchesSelectorScreen(
|
||||||
patches: List<PatchInfo>,
|
patches: List<PatchInfo>,
|
||||||
filterFlag: Int,
|
filterFlag: Int,
|
||||||
supported: Boolean,
|
supported: Boolean,
|
||||||
header: (@Composable () -> Unit)? = null
|
header: (@Composable () -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
if (patches.isNotEmpty() && (vm.filter and filterFlag) != 0 || vm.filter == 0) {
|
if (patches.isNotEmpty() && (vm.filter and filterFlag) != 0 || vm.filter == 0) {
|
||||||
header?.let {
|
header?.let {
|
||||||
|
@ -162,17 +165,19 @@ fun PatchesSelectorScreen(
|
||||||
|
|
||||||
items(
|
items(
|
||||||
items = patches,
|
items = patches,
|
||||||
key = { it.name }
|
key = { it.name },
|
||||||
) { patch ->
|
) { patch ->
|
||||||
PatchItem(
|
PatchItem(
|
||||||
patch = patch,
|
patch = patch,
|
||||||
onOptionsDialog = {
|
onOptionsDialog = {
|
||||||
vm.optionsDialog = uid to patch
|
vm.optionsDialog = uid to patch
|
||||||
},
|
},
|
||||||
selected = supported && vm.isSelected(
|
selected =
|
||||||
uid,
|
supported &&
|
||||||
patch
|
vm.isSelected(
|
||||||
),
|
uid,
|
||||||
|
patch,
|
||||||
|
),
|
||||||
onToggle = {
|
onToggle = {
|
||||||
if (vm.selectionWarningEnabled) {
|
if (vm.selectionWarningEnabled) {
|
||||||
showSelectionWarning = true
|
showSelectionWarning = true
|
||||||
|
@ -182,7 +187,7 @@ fun PatchesSelectorScreen(
|
||||||
vm.togglePatch(uid, patch)
|
vm.togglePatch(uid, patch)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
supported = supported
|
supported = supported,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,28 +198,29 @@ fun PatchesSelectorScreen(
|
||||||
query = query,
|
query = query,
|
||||||
onQueryChange = { search = it },
|
onQueryChange = { search = it },
|
||||||
onActiveChange = { if (!it) search = null },
|
onActiveChange = { if (!it) search = null },
|
||||||
placeholder = { Text(stringResource(R.string.search_patches)) }
|
placeholder = { Text(stringResource(R.string.search_patches)) },
|
||||||
) {
|
) {
|
||||||
val bundle = bundles[pagerState.currentPage]
|
val bundle = bundles[pagerState.currentPage]
|
||||||
|
|
||||||
LazyColumnWithScrollbar(
|
LazyColumnWithScrollbar(
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
fun List<PatchInfo>.searched() = filter {
|
fun List<PatchInfo>.searched() =
|
||||||
it.name.contains(query, true)
|
filter {
|
||||||
}
|
it.name.contains(query, true)
|
||||||
|
}
|
||||||
|
|
||||||
patchList(
|
patchList(
|
||||||
uid = bundle.uid,
|
uid = bundle.uid,
|
||||||
patches = bundle.supported.searched(),
|
patches = bundle.supported.searched(),
|
||||||
filterFlag = SHOW_SUPPORTED,
|
filterFlag = SHOW_SUPPORTED,
|
||||||
supported = true
|
supported = true,
|
||||||
)
|
)
|
||||||
patchList(
|
patchList(
|
||||||
uid = bundle.uid,
|
uid = bundle.uid,
|
||||||
patches = bundle.universal.searched(),
|
patches = bundle.universal.searched(),
|
||||||
filterFlag = SHOW_UNIVERSAL,
|
filterFlag = SHOW_UNIVERSAL,
|
||||||
supported = true
|
supported = true,
|
||||||
) {
|
) {
|
||||||
ListHeader(
|
ListHeader(
|
||||||
title = stringResource(R.string.universal_patches),
|
title = stringResource(R.string.universal_patches),
|
||||||
|
@ -226,11 +232,11 @@ fun PatchesSelectorScreen(
|
||||||
uid = bundle.uid,
|
uid = bundle.uid,
|
||||||
patches = bundle.unsupported.searched(),
|
patches = bundle.unsupported.searched(),
|
||||||
filterFlag = SHOW_UNSUPPORTED,
|
filterFlag = SHOW_UNSUPPORTED,
|
||||||
supported = true
|
supported = true,
|
||||||
) {
|
) {
|
||||||
ListHeader(
|
ListHeader(
|
||||||
title = stringResource(R.string.unsupported_patches),
|
title = stringResource(R.string.unsupported_patches),
|
||||||
onHelpClick = { vm.openUnsupportedDialog(bundle.unsupported) }
|
onHelpClick = { vm.openUnsupportedDialog(bundle.unsupported) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,11 +258,11 @@ fun PatchesSelectorScreen(
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
search = ""
|
search = ""
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
Icon(Icons.Outlined.Search, stringResource(R.string.search))
|
Icon(Icons.Outlined.Search, stringResource(R.string.search))
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
|
@ -269,19 +275,19 @@ fun PatchesSelectorScreen(
|
||||||
onClick = {
|
onClick = {
|
||||||
// TODO: only allow this if all required options have been set.
|
// TODO: only allow this if all required options have been set.
|
||||||
onSave(vm.getCustomSelection(), vm.getOptions())
|
onSave(vm.getCustomSelection(), vm.getOptions())
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(paddingValues)
|
.padding(paddingValues),
|
||||||
) {
|
) {
|
||||||
if (bundles.size > 1) {
|
if (bundles.size > 1) {
|
||||||
ScrollableTabRow(
|
ScrollableTabRow(
|
||||||
selectedTabIndex = pagerState.currentPage,
|
selectedTabIndex = pagerState.currentPage,
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp)
|
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp),
|
||||||
) {
|
) {
|
||||||
bundles.forEachIndexed { index, bundle ->
|
bundles.forEachIndexed { index, bundle ->
|
||||||
HapticTab(
|
HapticTab(
|
||||||
|
@ -289,13 +295,13 @@ fun PatchesSelectorScreen(
|
||||||
onClick = {
|
onClick = {
|
||||||
composableScope.launch {
|
composableScope.launch {
|
||||||
pagerState.animateScrollToPage(
|
pagerState.animateScrollToPage(
|
||||||
index
|
index,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
text = { Text(bundle.name) },
|
text = { Text(bundle.name) },
|
||||||
selectedContentColor = MaterialTheme.colorScheme.primary,
|
selectedContentColor = MaterialTheme.colorScheme.primary,
|
||||||
unselectedContentColor = MaterialTheme.colorScheme.onSurfaceVariant
|
unselectedContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,19 +317,19 @@ fun PatchesSelectorScreen(
|
||||||
|
|
||||||
LazyColumnWithScrollbar(
|
LazyColumnWithScrollbar(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
state = patchLazyListStates[index]
|
state = patchLazyListStates[index],
|
||||||
) {
|
) {
|
||||||
patchList(
|
patchList(
|
||||||
uid = bundle.uid,
|
uid = bundle.uid,
|
||||||
patches = bundle.supported,
|
patches = bundle.supported,
|
||||||
filterFlag = SHOW_SUPPORTED,
|
filterFlag = SHOW_SUPPORTED,
|
||||||
supported = true
|
supported = true,
|
||||||
)
|
)
|
||||||
patchList(
|
patchList(
|
||||||
uid = bundle.uid,
|
uid = bundle.uid,
|
||||||
patches = bundle.universal,
|
patches = bundle.universal,
|
||||||
filterFlag = SHOW_UNIVERSAL,
|
filterFlag = SHOW_UNIVERSAL,
|
||||||
supported = true
|
supported = true,
|
||||||
) {
|
) {
|
||||||
ListHeader(
|
ListHeader(
|
||||||
title = stringResource(R.string.universal_patches),
|
title = stringResource(R.string.universal_patches),
|
||||||
|
@ -333,15 +339,15 @@ fun PatchesSelectorScreen(
|
||||||
uid = bundle.uid,
|
uid = bundle.uid,
|
||||||
patches = bundle.unsupported,
|
patches = bundle.unsupported,
|
||||||
filterFlag = SHOW_UNSUPPORTED,
|
filterFlag = SHOW_UNSUPPORTED,
|
||||||
supported = vm.allowIncompatiblePatches
|
supported = vm.allowIncompatiblePatches,
|
||||||
) {
|
) {
|
||||||
ListHeader(
|
ListHeader(
|
||||||
title = stringResource(R.string.unsupported_patches),
|
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
|
@Composable
|
||||||
fun UniversalPatchWarningDialog(
|
fun UniversalPatchWarningDialog(
|
||||||
onCancel: () -> Unit,
|
onCancel: () -> Unit,
|
||||||
onConfirm: () -> Unit
|
onConfirm: () -> Unit,
|
||||||
) {
|
) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onCancel,
|
onDismissRequest = onCancel,
|
||||||
|
@ -379,12 +385,12 @@ fun UniversalPatchWarningDialog(
|
||||||
title = {
|
title = {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.warning),
|
text = stringResource(R.string.warning),
|
||||||
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center)
|
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
text = {
|
text = {
|
||||||
Text(stringResource(R.string.universal_patch_warning_description))
|
Text(stringResource(R.string.universal_patch_warning_description))
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,17 +400,18 @@ fun PatchItem(
|
||||||
onOptionsDialog: () -> Unit,
|
onOptionsDialog: () -> Unit,
|
||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
onToggle: () -> Unit,
|
onToggle: () -> Unit,
|
||||||
supported: Boolean = true
|
supported: Boolean = true,
|
||||||
) = ListItem (
|
) = ListItem(
|
||||||
modifier = Modifier
|
modifier =
|
||||||
.let { if (!supported) it.alpha(0.5f) else it }
|
Modifier
|
||||||
.clickable(enabled = supported, onClick = onToggle)
|
.let { if (!supported) it.alpha(0.5f) else it }
|
||||||
.fillMaxSize(),
|
.clickable(enabled = supported, onClick = onToggle)
|
||||||
|
.fillMaxSize(),
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
HapticCheckbox(
|
HapticCheckbox(
|
||||||
checked = selected,
|
checked = selected,
|
||||||
onCheckedChange = { onToggle() },
|
onCheckedChange = { onToggle() },
|
||||||
enabled = supported
|
enabled = supported,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
headlineContent = { Text(patch.name) },
|
headlineContent = { Text(patch.name) },
|
||||||
|
@ -421,26 +428,27 @@ fun PatchItem(
|
||||||
@Composable
|
@Composable
|
||||||
fun ListHeader(
|
fun ListHeader(
|
||||||
title: String,
|
title: String,
|
||||||
onHelpClick: (() -> Unit)? = null
|
onHelpClick: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = MaterialTheme.colorScheme.primary,
|
||||||
style = MaterialTheme.typography.labelLarge
|
style = MaterialTheme.typography.labelLarge,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
trailingContent = onHelpClick?.let {
|
trailingContent =
|
||||||
{
|
onHelpClick?.let {
|
||||||
IconButton(onClick = it) {
|
{
|
||||||
Icon(
|
IconButton(onClick = it) {
|
||||||
Icons.AutoMirrored.Outlined.HelpOutline,
|
Icon(
|
||||||
stringResource(R.string.help)
|
Icons.AutoMirrored.Outlined.HelpOutline,
|
||||||
)
|
stringResource(R.string.help),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,7 +456,7 @@ fun ListHeader(
|
||||||
fun UnsupportedDialog(
|
fun UnsupportedDialog(
|
||||||
appVersion: String,
|
appVersion: String,
|
||||||
supportedVersions: List<String>,
|
supportedVersions: List<String>,
|
||||||
onDismissRequest: () -> Unit
|
onDismissRequest: () -> Unit,
|
||||||
) = AlertDialog(
|
) = AlertDialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
|
@ -462,10 +470,10 @@ fun UnsupportedDialog(
|
||||||
stringResource(
|
stringResource(
|
||||||
R.string.app_not_supported,
|
R.string.app_not_supported,
|
||||||
appVersion,
|
appVersion,
|
||||||
supportedVersions.joinToString(", ")
|
supportedVersions.joinToString(", "),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@ -478,10 +486,11 @@ fun OptionsDialog(
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
) = Dialog(
|
) = Dialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
properties = DialogProperties(
|
properties =
|
||||||
usePlatformDefaultWidth = false,
|
DialogProperties(
|
||||||
dismissOnBackPress = true
|
usePlatformDefaultWidth = false,
|
||||||
)
|
dismissOnBackPress = true,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
|
@ -492,12 +501,12 @@ fun OptionsDialog(
|
||||||
IconButton(onClick = reset) {
|
IconButton(onClick = reset) {
|
||||||
Icon(Icons.Outlined.Restore, stringResource(R.string.reset))
|
Icon(Icons.Outlined.Restore, stringResource(R.string.reset))
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
LazyColumnWithScrollbar(
|
LazyColumnWithScrollbar(
|
||||||
modifier = Modifier.padding(paddingValues)
|
modifier = Modifier.padding(paddingValues),
|
||||||
) {
|
) {
|
||||||
if (patch.options == null) return@LazyColumnWithScrollbar
|
if (patch.options == null) return@LazyColumnWithScrollbar
|
||||||
|
|
||||||
|
@ -511,4 +520,4 @@ fun OptionsDialog(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.outlined.ArrowRight
|
import androidx.compose.material.icons.automirrored.outlined.ArrowRight
|
||||||
import androidx.compose.material.icons.filled.AutoFixHigh
|
import androidx.compose.material.icons.filled.AutoFixHigh
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.MaterialTheme
|
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.AppInfo
|
||||||
import app.revanced.manager.ui.component.AppTopBar
|
import app.revanced.manager.ui.component.AppTopBar
|
||||||
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
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.destination.SelectedAppInfoDestination
|
||||||
import app.revanced.manager.ui.model.BundleInfo.Extensions.bundleInfoFlow
|
import app.revanced.manager.ui.model.BundleInfo.Extensions.bundleInfoFlow
|
||||||
import app.revanced.manager.ui.model.SelectedApp
|
import app.revanced.manager.ui.model.SelectedApp
|
||||||
|
@ -48,7 +48,7 @@ import org.koin.core.parameter.parametersOf
|
||||||
fun SelectedAppInfoScreen(
|
fun SelectedAppInfoScreen(
|
||||||
onPatchClick: (SelectedApp, PatchSelection, Options) -> Unit,
|
onPatchClick: (SelectedApp, PatchSelection, Options) -> Unit,
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
vm: SelectedAppInfoViewModel
|
vm: SelectedAppInfoViewModel,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
@ -82,67 +82,71 @@ fun SelectedAppInfoScreen(
|
||||||
|
|
||||||
AnimatedNavHost(controller = navController) { destination ->
|
AnimatedNavHost(controller = navController) { destination ->
|
||||||
when (destination) {
|
when (destination) {
|
||||||
is SelectedAppInfoDestination.Main -> SelectedAppInfoScreen(
|
is SelectedAppInfoDestination.Main ->
|
||||||
onPatchClick = patchClick@{
|
SelectedAppInfoScreen(
|
||||||
if (selectedPatchCount == 0) {
|
onPatchClick = patchClick@{
|
||||||
context.toast(context.getString(R.string.no_patches_selected))
|
if (selectedPatchCount == 0) {
|
||||||
|
context.toast(context.getString(R.string.no_patches_selected))
|
||||||
|
|
||||||
return@patchClick
|
return@patchClick
|
||||||
}
|
}
|
||||||
onPatchClick(
|
onPatchClick(
|
||||||
vm.selectedApp,
|
|
||||||
patches,
|
|
||||||
vm.getOptionsFiltered(bundles)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onPatchSelectorClick = {
|
|
||||||
navController.navigate(
|
|
||||||
SelectedAppInfoDestination.PatchesSelector(
|
|
||||||
vm.selectedApp,
|
vm.selectedApp,
|
||||||
vm.getCustomPatches(
|
patches,
|
||||||
bundles,
|
vm.getOptionsFiltered(bundles),
|
||||||
allowIncompatiblePatches
|
)
|
||||||
|
},
|
||||||
|
onPatchSelectorClick = {
|
||||||
|
navController.navigate(
|
||||||
|
SelectedAppInfoDestination.PatchesSelector(
|
||||||
|
vm.selectedApp,
|
||||||
|
vm.getCustomPatches(
|
||||||
|
bundles,
|
||||||
|
allowIncompatiblePatches,
|
||||||
|
),
|
||||||
|
vm.options,
|
||||||
),
|
),
|
||||||
vm.options
|
|
||||||
)
|
)
|
||||||
)
|
},
|
||||||
},
|
onVersionSelectorClick = {
|
||||||
onVersionSelectorClick = {
|
navController.navigate(SelectedAppInfoDestination.VersionSelector)
|
||||||
navController.navigate(SelectedAppInfoDestination.VersionSelector)
|
},
|
||||||
},
|
onBackClick = onBackClick,
|
||||||
onBackClick = onBackClick,
|
availablePatchCount = availablePatchCount,
|
||||||
availablePatchCount = availablePatchCount,
|
selectedPatchCount = selectedPatchCount,
|
||||||
selectedPatchCount = selectedPatchCount,
|
packageName = packageName,
|
||||||
packageName = packageName,
|
version = version,
|
||||||
version = version,
|
packageInfo = vm.selectedAppInfo,
|
||||||
packageInfo = vm.selectedAppInfo,
|
)
|
||||||
)
|
|
||||||
|
|
||||||
is SelectedAppInfoDestination.VersionSelector -> VersionSelectorScreen(
|
is SelectedAppInfoDestination.VersionSelector ->
|
||||||
onBackClick = navController::pop,
|
VersionSelectorScreen(
|
||||||
onAppClick = {
|
onBackClick = navController::pop,
|
||||||
vm.selectedApp = it
|
onAppClick = {
|
||||||
navController.pop()
|
vm.selectedApp = it
|
||||||
},
|
navController.pop()
|
||||||
viewModel = koinViewModel { parametersOf(packageName) }
|
},
|
||||||
)
|
viewModel = koinViewModel { parametersOf(packageName) },
|
||||||
|
)
|
||||||
|
|
||||||
is SelectedAppInfoDestination.PatchesSelector -> PatchesSelectorScreen(
|
is SelectedAppInfoDestination.PatchesSelector ->
|
||||||
onSave = { patches, options ->
|
PatchesSelectorScreen(
|
||||||
vm.updateConfiguration(patches, options, bundles)
|
onSave = { patches, options ->
|
||||||
navController.pop()
|
vm.updateConfiguration(patches, options, bundles)
|
||||||
},
|
navController.pop()
|
||||||
onBackClick = navController::pop,
|
},
|
||||||
vm = koinViewModel {
|
onBackClick = navController::pop,
|
||||||
parametersOf(
|
vm =
|
||||||
PatchesSelectorViewModel.Params(
|
koinViewModel {
|
||||||
destination.app,
|
parametersOf(
|
||||||
destination.currentSelection,
|
PatchesSelectorViewModel.Params(
|
||||||
destination.options,
|
destination.app,
|
||||||
)
|
destination.currentSelection,
|
||||||
)
|
destination.options,
|
||||||
}
|
),
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,21 +168,22 @@ private fun SelectedAppInfoScreen(
|
||||||
topBar = {
|
topBar = {
|
||||||
AppTopBar(
|
AppTopBar(
|
||||||
title = stringResource(R.string.app_info),
|
title = stringResource(R.string.app_info),
|
||||||
onBackClick = onBackClick
|
onBackClick = onBackClick,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
ExtendedFloatingActionButton(
|
HapticExtendedFloatingActionButton(
|
||||||
text = { Text(stringResource(R.string.patch)) },
|
text = { Text(stringResource(R.string.patch)) },
|
||||||
icon = { Icon(Icons.Default.AutoFixHigh, null) },
|
icon = { Icon(Icons.Default.AutoFixHigh, null) },
|
||||||
onClick = onPatchClick
|
onClick = onPatchClick,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
ColumnWithScrollbar(
|
ColumnWithScrollbar(
|
||||||
modifier = Modifier
|
modifier =
|
||||||
.fillMaxSize()
|
Modifier
|
||||||
.padding(paddingValues)
|
.fillMaxSize()
|
||||||
|
.padding(paddingValues),
|
||||||
) {
|
) {
|
||||||
AppInfo(packageInfo, placeholderLabel = packageName) {
|
AppInfo(packageInfo, placeholderLabel = packageName) {
|
||||||
Text(
|
Text(
|
||||||
|
@ -191,39 +196,44 @@ private fun SelectedAppInfoScreen(
|
||||||
PageItem(
|
PageItem(
|
||||||
R.string.patch_selector_item,
|
R.string.patch_selector_item,
|
||||||
stringResource(R.string.patch_selector_item_description, selectedPatchCount),
|
stringResource(R.string.patch_selector_item_description, selectedPatchCount),
|
||||||
onPatchSelectorClick
|
onPatchSelectorClick,
|
||||||
)
|
)
|
||||||
PageItem(
|
PageItem(
|
||||||
R.string.version_selector_item,
|
R.string.version_selector_item,
|
||||||
stringResource(R.string.version_selector_item_description, version),
|
stringResource(R.string.version_selector_item_description, version),
|
||||||
onVersionSelectorClick
|
onVersionSelectorClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun PageItem(@StringRes title: Int, description: String, onClick: () -> Unit) {
|
private fun PageItem(
|
||||||
|
@StringRes title: Int,
|
||||||
|
description: String,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier
|
modifier =
|
||||||
.clickable(onClick = onClick)
|
Modifier
|
||||||
.padding(start = 8.dp),
|
.clickable(onClick = onClick)
|
||||||
|
.padding(start = 8.dp),
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text(
|
Text(
|
||||||
stringResource(title),
|
stringResource(title),
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
style = MaterialTheme.typography.titleLarge
|
style = MaterialTheme.typography.titleLarge,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
supportingContent = {
|
supportingContent = {
|
||||||
Text(
|
Text(
|
||||||
description,
|
description,
|
||||||
color = MaterialTheme.colorScheme.outline,
|
color = MaterialTheme.colorScheme.outline,
|
||||||
style = MaterialTheme.typography.bodyMedium
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
Icon(Icons.AutoMirrored.Outlined.ArrowRight, null)
|
Icon(Icons.AutoMirrored.Outlined.ArrowRight, null)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.material3.Checkbox
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
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.AppTopBar
|
||||||
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
||||||
import app.revanced.manager.ui.component.GroupHeader
|
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.BooleanItem
|
||||||
import app.revanced.manager.ui.component.settings.SettingsListItem
|
import app.revanced.manager.ui.component.settings.SettingsListItem
|
||||||
import app.revanced.manager.ui.viewmodel.DownloadsViewModel
|
import app.revanced.manager.ui.viewmodel.DownloadsViewModel
|
||||||
|
@ -29,7 +29,7 @@ import org.koin.androidx.compose.koinViewModel
|
||||||
@Composable
|
@Composable
|
||||||
fun DownloadsSettingsScreen(
|
fun DownloadsSettingsScreen(
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
viewModel: DownloadsViewModel = koinViewModel()
|
viewModel: DownloadsViewModel = koinViewModel(),
|
||||||
) {
|
) {
|
||||||
val prefs = viewModel.prefs
|
val prefs = viewModel.prefs
|
||||||
|
|
||||||
|
@ -46,14 +46,15 @@ fun DownloadsSettingsScreen(
|
||||||
Icon(Icons.Default.Delete, stringResource(R.string.delete))
|
Icon(Icons.Default.Delete, stringResource(R.string.delete))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
ColumnWithScrollbar(
|
ColumnWithScrollbar(
|
||||||
modifier = Modifier
|
modifier =
|
||||||
.fillMaxSize()
|
Modifier
|
||||||
.padding(paddingValues)
|
.fillMaxSize()
|
||||||
|
.padding(paddingValues),
|
||||||
) {
|
) {
|
||||||
BooleanItem(
|
BooleanItem(
|
||||||
preference = prefs.preferSplits,
|
preference = prefs.preferSplits,
|
||||||
|
@ -69,16 +70,21 @@ fun DownloadsSettingsScreen(
|
||||||
SettingsListItem(
|
SettingsListItem(
|
||||||
modifier = Modifier.clickable { viewModel.toggleItem(app) },
|
modifier = Modifier.clickable { viewModel.toggleItem(app) },
|
||||||
headlineContent = app.packageName,
|
headlineContent = app.packageName,
|
||||||
leadingContent = (@Composable {
|
leadingContent =
|
||||||
Checkbox(
|
{
|
||||||
checked = selected,
|
(
|
||||||
onCheckedChange = { viewModel.toggleItem(app) }
|
@Composable {
|
||||||
)
|
HapticCheckbox(
|
||||||
}).takeIf { viewModel.selection.isNotEmpty() },
|
checked = selected,
|
||||||
|
onCheckedChange = { viewModel.toggleItem(app) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
).takeIf { viewModel.selection.isNotEmpty() }
|
||||||
|
},
|
||||||
supportingContent = app.version,
|
supportingContent = app.version,
|
||||||
tonalElevation = if (selected) 8.dp else 0.dp
|
tonalElevation = if (selected) 8.dp else 0.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue