Use bottom sheet for websearch dialog

This commit is contained in:
MM20 2022-06-12 20:02:14 +02:00
parent c8d34ab18a
commit c247878c6d
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389

View File

@ -3,17 +3,12 @@ package de.mm20.launcher2.ui.settings.websearch
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Canvas import androidx.compose.foundation.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.TextField
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.* import androidx.compose.material.icons.rounded.*
import androidx.compose.material3.* import androidx.compose.material3.*
@ -25,16 +20,14 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.* import androidx.compose.ui.graphics.*
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.rememberImagePainter import coil.compose.rememberImagePainter
import com.godaddy.android.colorpicker.ClassicColorPicker import com.godaddy.android.colorpicker.ClassicColorPicker
import de.mm20.launcher2.search.data.Websearch import de.mm20.launcher2.search.data.Websearch
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.BottomSheetDialog
import de.mm20.launcher2.ui.component.preferences.Preference import de.mm20.launcher2.ui.component.preferences.Preference
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
@ -139,7 +132,7 @@ fun WebsearchPreference(
} }
} }
@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun EditWebsearchDialog( fun EditWebsearchDialog(
title: String, title: String,
@ -149,7 +142,6 @@ fun EditWebsearchDialog(
onCancel: () -> Unit, onCancel: () -> Unit,
enableImport: Boolean = false enableImport: Boolean = false
) { ) {
val context = LocalContext.current
var showDropdown by remember { mutableStateOf(false) } var showDropdown by remember { mutableStateOf(false) }
var label by remember { mutableStateOf(value.label) } var label by remember { mutableStateOf(value.label) }
@ -179,279 +171,264 @@ fun EditWebsearchDialog(
} }
BottomSheetDialog(onDismissRequest = onCancel,
Dialog( title = { Text(title) },
onDismissRequest = onCancel, actions = {
properties = DialogProperties(usePlatformDefaultWidth = false) if (enableImport) {
) { Box {
Surface( IconButton(onClick = {
shape = RectangleShape, showImport = !showImport
modifier = Modifier.fillMaxSize() importError = false
) { }) {
Scaffold( Icon(
topBar = { imageVector = Icons.Rounded.CloudDownload,
SmallTopAppBar( contentDescription = null
navigationIcon = { )
IconButton(onClick = {
if (icon != value.icon) {
icon?.let { viewModel.deleteIcon(it) }
}
onCancel()
}) {
Icon(imageVector = Icons.Rounded.Close, contentDescription = null)
}
},
title = {
Text(
text = title
)
},
actions = {
IconButton(onClick = {
if (urlTemplate.contains("\${1}")) {
value.label = label
value.urlTemplate = urlTemplate
if (value.icon != icon) {
value.icon?.let {
viewModel.deleteIcon(it)
}
}
value.icon = icon
value.color = color
onValueSaved(value)
} else {
showError = true
}
}) {
Icon(imageVector = Icons.Rounded.Save, contentDescription = null)
}
if (enableImport) {
Box {
IconButton(onClick = {
showImport = !showImport
importError = false
}) {
Icon(
imageVector = Icons.Rounded.Download,
contentDescription = null
)
}
}
}
if (onValueDeleted != null) {
Box {
IconButton(onClick = {
showDropdown = true
}) {
Icon(
imageVector = Icons.Rounded.MoreVert,
contentDescription = null
)
}
DropdownMenu(
expanded = showDropdown,
onDismissRequest = { showDropdown = false }) {
DropdownMenuItem(
text = {
Text(
text = stringResource(R.string.menu_delete)
)
},
onClick = {
onValueDeleted(value)
onCancel()
})
}
}
}
}
)
}
) {
Column(modifier = Modifier.padding(it).padding(16.dp)) {
AnimatedVisibility(showImport) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 24.dp)
) {
Column(
modifier = Modifier
.padding(horizontal = 16.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
var importUrl by remember { mutableStateOf("") }
OutlinedTextField(
modifier = Modifier
.weight(1f)
.padding(bottom = 16.dp, top = 8.dp, end = 8.dp),
label = { Text(stringResource(R.string.websearch_dialog_import_url)) },
value = importUrl,
onValueChange = {
importUrl = it
importError = false
},
textStyle = MaterialTheme.typography.bodyLarge,
)
if (loadingImport) {
CircularProgressIndicator(
modifier = Modifier
.padding(12.dp)
.size(24.dp)
)
} else {
IconButton(onClick = {
scope.launch {
loadingImport = true
val websearch =
viewModel.importWebsearch(
importUrl,
iconSizePx
)
if (websearch != null) {
label = websearch.label
icon = websearch.icon
urlTemplate = websearch.urlTemplate
color = websearch.color
showImport = false
} else {
importError = true
}
loadingImport = false
}
}) {
Icon(
imageVector = Icons.Rounded.ArrowForward,
contentDescription = null
)
}
}
}
AnimatedVisibility(importError) {
Column(
modifier = Modifier.padding(bottom = 8.dp)
) {
Text(
text = stringResource(R.string.websearch_dialog_import_error),
style = MaterialTheme.typography.labelSmall
)
TextButton(
modifier = Modifier.align(Alignment.End),
onClick = { showImport = false }) {
Text(
text = stringResource(android.R.string.ok),
style = MaterialTheme.typography.labelMedium
)
}
}
}
}
}
} }
if (icon != null) { }
}
if (onValueDeleted != null) {
Box {
IconButton(onClick = {
showDropdown = true
}) {
Icon(
imageVector = Icons.Rounded.MoreVert,
contentDescription = null
)
}
DropdownMenu(
expanded = showDropdown,
onDismissRequest = { showDropdown = false }) {
DropdownMenuItem(
text = {
Text(
text = stringResource(R.string.menu_delete)
)
},
onClick = {
onValueDeleted(value)
onCancel()
})
}
}
}
},
confirmButton = {
Button(onClick = {
if (urlTemplate.contains("\${1}")) {
value.label = label
value.urlTemplate = urlTemplate
if (value.icon != icon) {
value.icon?.let {
viewModel.deleteIcon(it)
}
}
value.icon = icon
value.color = color
onValueSaved(value)
} else {
showError = true
}
}) {
Text("Save")
}
},
dismissButton = {
OutlinedButton(onClick = {
if (icon != value.icon) {
icon?.let { viewModel.deleteIcon(it) }
}
onCancel()
}) {
Text(stringResource(android.R.string.cancel))
}
}
) {
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState())
) {
AnimatedVisibility(showImport) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 24.dp)
) {
Column(
modifier = Modifier
.padding(horizontal = 16.dp)
) {
Row( Row(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Image( var importUrl by remember { mutableStateOf("") }
painter = rememberImagePainter(icon?.let { File(it) }), OutlinedTextField(
contentDescription = null,
modifier = Modifier modifier = Modifier
.padding(end = 16.dp) .weight(1f)
.size(48.dp) .padding(bottom = 16.dp, top = 8.dp, end = 8.dp),
label = { Text(stringResource(R.string.websearch_dialog_import_url)) },
value = importUrl,
onValueChange = {
importUrl = it
importError = false
},
textStyle = MaterialTheme.typography.bodyLarge,
) )
TextButton( if (loadingImport) {
onClick = { CircularProgressIndicator(
chooseIconLauncher.launch("image/*") modifier = Modifier
}, .padding(12.dp)
modifier = Modifier.padding(4.dp) .size(24.dp)
) {
Text(
stringResource(R.string.websearch_dialog_replace_icon),
)
}
TextButton(
onClick = {
icon = null
},
modifier = Modifier.padding(4.dp),
colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colorScheme.error
)
) {
Text(
stringResource(R.string.websearch_dialog_delete_icon),
) )
} else {
IconButton(onClick = {
scope.launch {
loadingImport = true
val websearch =
viewModel.importWebsearch(
importUrl,
iconSizePx
)
if (websearch != null) {
label = websearch.label
icon = websearch.icon
urlTemplate = websearch.urlTemplate
color = websearch.color
showImport = false
} else {
importError = true
}
loadingImport = false
}
}) {
Icon(
imageVector = Icons.Rounded.ArrowForward,
contentDescription = null
)
}
} }
} }
} else { AnimatedVisibility(importError) {
ColorPicker( Column(
value = color, modifier = Modifier.padding(bottom = 8.dp)
onColorSelected = { color = it } ) {
) Text(
TextButton( text = stringResource(R.string.websearch_dialog_import_error),
onClick = { style = MaterialTheme.typography.labelSmall
chooseIconLauncher.launch("image/*") )
}, TextButton(
modifier = Modifier modifier = Modifier.align(Alignment.End),
.padding(4.dp) onClick = { showImport = false }) {
.align(Alignment.End) Text(
) { text = stringResource(android.R.string.ok),
Text( style = MaterialTheme.typography.labelMedium
stringResource(R.string.websearch_dialog_custom_icon), )
) }
}
} }
} }
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(top = 24.dp),
value = label,
onValueChange = {
label = it
},
label = {
Text(text = stringResource(R.string.websearch_dialog_name))
},
textStyle = MaterialTheme.typography.bodyLarge,
)
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
value = urlTemplate,
onValueChange = {
urlTemplate = it
},
label = {
Text(text = stringResource(R.string.websearch_dialog_url))
},
textStyle = MaterialTheme.typography.bodyLarge,
)
AnimatedVisibility(showError) {
Text(
modifier = Modifier.padding(top = 8.dp),
text = stringResource(R.string.websearch_dialog_url_error),
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.error
)
}
Text(
modifier = Modifier.padding(top = 8.dp),
text = stringResource(R.string.websearch_dialog_url_description),
style = MaterialTheme.typography.labelMedium
)
} }
} }
if (icon != null) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = rememberImagePainter(icon?.let { File(it) }),
contentDescription = null,
modifier = Modifier
.padding(end = 16.dp)
.size(48.dp)
)
TextButton(
onClick = {
chooseIconLauncher.launch("image/*")
},
modifier = Modifier.padding(4.dp)
) {
Text(
stringResource(R.string.websearch_dialog_replace_icon),
)
}
TextButton(
onClick = {
icon = null
},
modifier = Modifier.padding(4.dp),
colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colorScheme.error
)
) {
Text(
stringResource(R.string.websearch_dialog_delete_icon),
)
}
}
} else {
ColorPicker(
value = color,
onColorSelected = { color = it }
)
TextButton(
onClick = {
chooseIconLauncher.launch("image/*")
},
modifier = Modifier
.padding(4.dp)
.align(Alignment.End)
) {
Text(
stringResource(R.string.websearch_dialog_custom_icon),
)
}
}
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(top = 24.dp),
value = label,
onValueChange = {
label = it
},
label = {
Text(text = stringResource(R.string.websearch_dialog_name))
},
textStyle = MaterialTheme.typography.bodyLarge,
)
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
value = urlTemplate,
onValueChange = {
urlTemplate = it
},
label = {
Text(text = stringResource(R.string.websearch_dialog_url))
},
textStyle = MaterialTheme.typography.bodyLarge,
)
AnimatedVisibility(showError) {
Text(
modifier = Modifier.padding(top = 8.dp),
text = stringResource(R.string.websearch_dialog_url_error),
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.error
)
}
Text(
modifier = Modifier.padding(top = 8.dp),
text = stringResource(R.string.websearch_dialog_url_description),
style = MaterialTheme.typography.labelMedium
)
} }
} }
} }
@Composable @Composable