From 1707a9690a00968849953684952d57fc2db7df16 Mon Sep 17 00:00:00 2001 From: Ax333l Date: Fri, 4 Aug 2023 12:46:07 +0200 Subject: [PATCH] feat: improve bundle dialog UI --- .../manager/ui/component/TextInputDialog.kt | 51 ++++++ .../ui/component/bundle/BaseBundleDialog.kt | 173 +++++++++++------- .../bundle/BundleInformationDialog.kt | 2 +- .../ui/component/bundle/BundleListItem.kt | 3 + .../ui/component/bundle/ImportBundleDialog.kt | 99 +++++----- app/src/main/res/values/strings.xml | 7 +- 6 files changed, 209 insertions(+), 126 deletions(-) create mode 100644 app/src/main/java/app/revanced/manager/ui/component/TextInputDialog.kt diff --git a/app/src/main/java/app/revanced/manager/ui/component/TextInputDialog.kt b/app/src/main/java/app/revanced/manager/ui/component/TextInputDialog.kt new file mode 100644 index 00000000..d0484f1e --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/component/TextInputDialog.kt @@ -0,0 +1,51 @@ +package app.revanced.manager.ui.component + +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.res.stringResource +import app.revanced.manager.R + +@Composable +fun TextInputDialog( + initial: String, + title: String, + onDismissRequest: () -> Unit, + onConfirm: (String) -> Unit, + validator: (String) -> Boolean = String::isNotEmpty, +) { + val (value, setValue) = rememberSaveable(initial) { + mutableStateOf(initial) + } + val valid = remember(value, validator) { + validator(value) + } + + AlertDialog( + onDismissRequest = onDismissRequest, + confirmButton = { + TextButton( + onClick = { onConfirm(value) }, + enabled = valid + ) { + Text(stringResource(R.string.ok)) + } + }, + dismissButton = { + TextButton(onClick = onDismissRequest) { + Text(stringResource(R.string.cancel)) + } + }, + title = { + Text(title) + }, + text = { + TextField(value = value, onValueChange = setValue) + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/component/bundle/BaseBundleDialog.kt b/app/src/main/java/app/revanced/manager/ui/component/bundle/BaseBundleDialog.kt index 07ae4532..042449d2 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/bundle/BaseBundleDialog.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/bundle/BaseBundleDialog.kt @@ -1,5 +1,7 @@ package app.revanced.manager.ui.component.bundle +import android.webkit.URLUtil +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxWidth @@ -12,23 +14,27 @@ import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +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.unit.dp import app.revanced.manager.R +import app.revanced.manager.ui.component.TextInputDialog @Composable fun BaseBundleDialog( modifier: Modifier = Modifier, isDefault: Boolean, name: String, - onNameChange: (String) -> Unit = {}, + onNameChange: ((String) -> Unit)? = null, remoteUrl: String?, - onRemoteUrlChange: (String) -> Unit = {}, + onRemoteUrlChange: ((String) -> Unit)? = null, patchCount: Int, version: String?, autoUpdate: Boolean, @@ -40,79 +46,108 @@ fun BaseBundleDialog( modifier = Modifier .fillMaxWidth() .verticalScroll(rememberScrollState()) - .then(modifier) -) { - Column( - modifier = Modifier.padding( - start = 24.dp, - top = 16.dp, - end = 24.dp, - ) - ) { - OutlinedTextField( - modifier = Modifier - .fillMaxWidth() - .padding(bottom = 16.dp), - value = name, - onValueChange = onNameChange, - label = { - Text(stringResource(R.string.bundle_input_name)) - } - ) - remoteUrl?.takeUnless { isDefault }?.let { - OutlinedTextField( - modifier = Modifier - .fillMaxWidth() - .padding(bottom = 16.dp), - value = it, - onValueChange = onRemoteUrlChange, - label = { - Text(stringResource(R.string.bundle_input_source_url)) - } - ) - } - - extraFields() - } - - Column( - Modifier.padding( + .padding( start = 8.dp, top = 8.dp, end = 4.dp, ) - ) Info@{ - if (remoteUrl != null) { - BundleListItem( - headlineText = stringResource(R.string.automatically_update), - supportingText = stringResource(R.string.automatically_update_description), - trailingContent = { - Switch( - checked = autoUpdate, - onCheckedChange = onAutoUpdateChange - ) + .then(modifier) +) { + var showNameInputDialog by rememberSaveable { + mutableStateOf(false) + } + if (showNameInputDialog) { + TextInputDialog( + initial = name, + title = stringResource(R.string.bundle_input_name), + onDismissRequest = { + showNameInputDialog = false + }, + onConfirm = { + showNameInputDialog = false + onNameChange?.invoke(it) + }, + validator = { + it.length in 1..19 + } + ) + } + BundleListItem( + headlineText = stringResource(R.string.bundle_input_name), + supportingText = name.ifEmpty { stringResource(R.string.field_not_set) }, + modifier = Modifier.clickable(enabled = onNameChange != null) { + showNameInputDialog = true + } + ) + + remoteUrl?.takeUnless { isDefault }?.let { url -> + var showUrlInputDialog by rememberSaveable { + mutableStateOf(false) + } + if (showUrlInputDialog) { + TextInputDialog( + initial = url, + title = stringResource(R.string.bundle_input_source_url), + onDismissRequest = { showUrlInputDialog = false }, + onConfirm = { + showUrlInputDialog = false + onRemoteUrlChange?.invoke(it) + }, + validator = { + if (it.isEmpty()) return@TextInputDialog false + + URLUtil.isValidUrl(it) } ) } BundleListItem( - headlineText = stringResource(R.string.bundle_type), - supportingText = stringResource(R.string.bundle_type_description) - ) { - FilledTonalButton( - onClick = onBundleTypeClick, - content = { - if (remoteUrl == null) { - Text(stringResource(R.string.local)) - } else { - Text(stringResource(R.string.remote)) - } - } - ) + modifier = Modifier.clickable(enabled = onRemoteUrlChange != null) { + showUrlInputDialog = true + }, + headlineText = stringResource(R.string.bundle_input_source_url), + supportingText = url.ifEmpty { stringResource(R.string.field_not_set) } + ) + } + + extraFields() + + if (remoteUrl != null) { + BundleListItem( + headlineText = stringResource(R.string.automatically_update), + supportingText = stringResource(R.string.automatically_update_description), + trailingContent = { + Switch( + checked = autoUpdate, + onCheckedChange = onAutoUpdateChange + ) + }, + modifier = Modifier.clickable { + onAutoUpdateChange(!autoUpdate) + } + ) + } + + BundleListItem( + headlineText = stringResource(R.string.bundle_type), + supportingText = stringResource(R.string.bundle_type_description), + modifier = Modifier.clickable { + onBundleTypeClick() } + ) { + FilledTonalButton( + onClick = onBundleTypeClick, + content = { + if (remoteUrl == null) { + Text(stringResource(R.string.local)) + } else { + Text(stringResource(R.string.remote)) + } + } + ) + } - if (version == null && patchCount < 1) return@Info - + if (version != null || patchCount > 0) { Text( text = stringResource(R.string.information), modifier = Modifier.padding( @@ -122,7 +157,9 @@ fun BaseBundleDialog( style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary, ) + } + if (patchCount > 0) { BundleListItem( headlineText = stringResource(R.string.patches), supportingText = if (patchCount == 0) stringResource(R.string.no_patches) @@ -138,12 +175,12 @@ fun BaseBundleDialog( } } ) + } - if (version == null) return@Info - + version?.let { BundleListItem( headlineText = stringResource(R.string.version), - supportingText = version, + supportingText = it, ) } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleInformationDialog.kt b/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleInformationDialog.kt index e2498e39..a34024fa 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleInformationDialog.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleInformationDialog.kt @@ -67,7 +67,7 @@ fun BundleInformationDialog( Scaffold( topBar = { BundleTopBar( - title = stringResource(R.string.bundle_information), + title = bundle.name, onBackClick = onDismissRequest, onBackIcon = { Icon( diff --git a/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleListItem.kt b/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleListItem.kt index 8ef9db10..48a1ac17 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleListItem.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleListItem.kt @@ -4,9 +4,11 @@ import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier @Composable fun BundleListItem( + modifier: Modifier = Modifier, headlineText: String, supportingText: String = "", trailingContent: @Composable (() -> Unit)? = null, @@ -26,5 +28,6 @@ fun BundleListItem( ) }, trailingContent = trailingContent, + modifier = modifier ) } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/component/bundle/ImportBundleDialog.kt b/app/src/main/java/app/revanced/manager/ui/component/bundle/ImportBundleDialog.kt index e854ae4c..d5bc9c03 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/bundle/ImportBundleDialog.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/bundle/ImportBundleDialog.kt @@ -1,10 +1,9 @@ package app.revanced.manager.ui.component.bundle import android.net.Uri -import android.webkit.URLUtil import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close @@ -12,7 +11,6 @@ import androidx.compose.material.icons.filled.Topic import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -46,17 +44,9 @@ fun ImportBundleDialog( var patchBundle by rememberSaveable { mutableStateOf(null) } var integrations by rememberSaveable { mutableStateOf(null) } - val patchBundleText = patchBundle?.toString().orEmpty() - val integrationText = integrations?.toString().orEmpty() - val inputsAreValid by remember { derivedStateOf { - val nameSize = name.length - when { - nameSize !in 1..19 -> false - isLocal -> patchBundle != null - else -> remoteUrl.isNotEmpty() && URLUtil.isValidUrl(remoteUrl) - } + name.isNotEmpty() && if (isLocal) patchBundle != null else remoteUrl.isNotEmpty() } } @@ -64,10 +54,17 @@ fun ImportBundleDialog( rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri -> uri?.let { patchBundle = it } } + fun launchPatchActivity() { + patchActivityLauncher.launch(JAR_MIMETYPE) + } + val integrationsActivityLauncher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri -> uri?.let { integrations = it } } + fun launchIntegrationsActivity() { + integrationsActivityLauncher.launch(APK_MIMETYPE) + } Dialog( onDismissRequest = onDismissRequest, @@ -123,53 +120,43 @@ fun ImportBundleDialog( onPatchesClick = {}, onBundleTypeClick = { isLocal = !isLocal }, ) { - if (isLocal) { - OutlinedTextField( - modifier = Modifier - .fillMaxWidth() - .padding(bottom = 16.dp), - value = patchBundleText, - onValueChange = {}, - label = { - Text("Patches Source File") - }, - trailingIcon = { - IconButton( - onClick = { - patchActivityLauncher.launch(JAR_MIMETYPE) - } - ) { - Icon( - imageVector = Icons.Default.Topic, - contentDescription = null - ) - } + if (!isLocal) return@BaseBundleDialog + + BundleListItem( + headlineText = stringResource(R.string.patch_bundle_field), + supportingText = 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() + } + ) - OutlinedTextField( - modifier = Modifier - .fillMaxWidth() - .padding(bottom = 16.dp), - value = integrationText, - onValueChange = {}, - label = { - Text("Integrations Source File") - }, - trailingIcon = { - IconButton( - onClick = { - integrationsActivityLauncher.launch(APK_MIMETYPE) - } - ) { - Icon( - imageVector = Icons.Default.Topic, - contentDescription = null - ) - } + BundleListItem( + headlineText = stringResource(R.string.integrations_field), + supportingText = 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() + } + ) } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3fd66f17..0ad09c51 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,8 +13,13 @@ Import Import patch bundle - Bundle information Bundle patches + Patch bundle + Integrations + Provided + Not provided + + Not set Missing Error