Add option to import websearches using the OpenSearch standard
Implements #31
This commit is contained in:
+108
-3
@@ -85,7 +85,8 @@ fun WebSearchSettingsScreen() {
|
||||
},
|
||||
onCancel = {
|
||||
showNewDialog = false
|
||||
}
|
||||
},
|
||||
enableImport = true
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -145,7 +146,8 @@ fun EditWebsearchDialog(
|
||||
value: Websearch,
|
||||
onValueSaved: (Websearch) -> Unit,
|
||||
onValueDeleted: ((Websearch) -> Unit)? = null,
|
||||
onCancel: () -> Unit
|
||||
onCancel: () -> Unit,
|
||||
enableImport: Boolean = false
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var showDropdown by remember { mutableStateOf(false) }
|
||||
@@ -158,6 +160,10 @@ fun EditWebsearchDialog(
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
var showImport by remember { mutableStateOf(false) }
|
||||
var loadingImport by remember { mutableStateOf(false) }
|
||||
var importError by remember { mutableStateOf(false) }
|
||||
|
||||
val viewModel: WebSearchSettingsScreenVM = viewModel()
|
||||
|
||||
val iconSizePx = 32.dp.toPixels().toInt()
|
||||
@@ -167,11 +173,13 @@ fun EditWebsearchDialog(
|
||||
) {
|
||||
if (it != null) {
|
||||
scope.launch {
|
||||
icon = viewModel.createIcon(context, it, iconSizePx)
|
||||
icon = viewModel.createIcon(it, iconSizePx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = onCancel,
|
||||
properties = DialogProperties(usePlatformDefaultWidth = false)
|
||||
@@ -217,6 +225,20 @@ fun EditWebsearchDialog(
|
||||
}) {
|
||||
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 = {
|
||||
@@ -248,6 +270,88 @@ fun EditWebsearchDialog(
|
||||
}
|
||||
) {
|
||||
Column(modifier = Modifier.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
|
||||
}
|
||||
)
|
||||
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) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
@@ -347,6 +451,7 @@ fun EditWebsearchDialog(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
+6
-14
@@ -45,20 +45,12 @@ class WebSearchSettingsScreenVM: ViewModel(), KoinComponent {
|
||||
* Read a user-selected icon, scale it down and copy it to the app's data dir
|
||||
* @return the absolute path of the copied file
|
||||
*/
|
||||
suspend fun createIcon(context: Context, uri: Uri, size: Int): String? = withContext(
|
||||
Dispatchers.IO) {
|
||||
val file = File(context.dataDir, System.currentTimeMillis().toString())
|
||||
val imageRequest = ImageRequest.Builder(context)
|
||||
.data(uri)
|
||||
.size(size)
|
||||
.scale(Scale.FIT)
|
||||
.build()
|
||||
val drawable = context.imageLoader.execute(imageRequest).drawable ?: return@withContext null
|
||||
val scaledIcon = drawable.toBitmap()
|
||||
val out = FileOutputStream(file)
|
||||
scaledIcon.compress(Bitmap.CompressFormat.PNG, 100, out)
|
||||
out.close()
|
||||
return@withContext file.absolutePath
|
||||
suspend fun createIcon(uri: Uri, size: Int): String? {
|
||||
return repository.createIcon(uri, size)
|
||||
}
|
||||
|
||||
suspend fun importWebsearch(url: String, size: Int): Websearch? {
|
||||
return repository.importWebsearch(url, size)
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user