Add multiple options for websearch query encoding

Close #174
This commit is contained in:
MM20 2022-09-25 20:59:29 +02:00
parent 69876e8884
commit 24e25148f0
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
10 changed files with 627 additions and 37 deletions

View File

@ -185,7 +185,7 @@ class BackupManager(
companion object { companion object {
private const val BackupFormatMajor = 1 private const val BackupFormatMajor = 1
private const val BackupFormatMinor = 1 private const val BackupFormatMinor = 2
internal const val BackupFormat = "$BackupFormatMajor.$BackupFormatMinor" internal const val BackupFormat = "$BackupFormatMajor.$BackupFormatMinor"
} }
} }

View File

@ -0,0 +1,450 @@
{
"formatVersion": 1,
"database": {
"version": 17,
"identityHash": "3321ba63cb650a091b1ca102198a41ba",
"entities": [
{
"tableName": "forecasts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `temperature` REAL NOT NULL, `minTemp` REAL NOT NULL, `maxTemp` REAL NOT NULL, `pressure` REAL NOT NULL, `humidity` REAL NOT NULL, `icon` INTEGER NOT NULL, `condition` TEXT NOT NULL, `clouds` INTEGER NOT NULL, `windSpeed` REAL NOT NULL, `windDirection` REAL NOT NULL, `rain` REAL NOT NULL, `snow` REAL NOT NULL, `night` INTEGER NOT NULL, `location` TEXT NOT NULL, `provider` TEXT NOT NULL, `providerUrl` TEXT NOT NULL, `rainProbability` INTEGER NOT NULL, `snowProbability` INTEGER NOT NULL, `updateTime` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
"fields": [
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "temperature",
"columnName": "temperature",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "minTemp",
"columnName": "minTemp",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "maxTemp",
"columnName": "maxTemp",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "pressure",
"columnName": "pressure",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "humidity",
"columnName": "humidity",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "icon",
"columnName": "icon",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "condition",
"columnName": "condition",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "clouds",
"columnName": "clouds",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "windSpeed",
"columnName": "windSpeed",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "windDirection",
"columnName": "windDirection",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "precipitation",
"columnName": "rain",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "snow",
"columnName": "snow",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "night",
"columnName": "night",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "location",
"columnName": "location",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "provider",
"columnName": "provider",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "providerUrl",
"columnName": "providerUrl",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "precipProbability",
"columnName": "rainProbability",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "snowProbability",
"columnName": "snowProbability",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "updateTime",
"columnName": "updateTime",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"timestamp"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "Searchable",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `searchable` TEXT NOT NULL, `launchCount` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `hidden` INTEGER NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serializedSearchable",
"columnName": "searchable",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "launchCount",
"columnName": "launchCount",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pinPosition",
"columnName": "pinned",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "hidden",
"columnName": "hidden",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"key"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "Websearch",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`urlTemplate` TEXT NOT NULL, `label` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `encoding` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"fields": [
{
"fieldPath": "urlTemplate",
"columnName": "urlTemplate",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "label",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "color",
"columnName": "color",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "icon",
"columnName": "icon",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "encoding",
"columnName": "encoding",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "Currency",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`symbol` TEXT NOT NULL, `value` REAL NOT NULL, `lastUpdate` INTEGER NOT NULL, PRIMARY KEY(`symbol`))",
"fields": [
{
"fieldPath": "symbol",
"columnName": "symbol",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "lastUpdate",
"columnName": "lastUpdate",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"symbol"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "Icons",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `componentName` TEXT, `drawable` TEXT, `iconPack` TEXT NOT NULL, `scale` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"fields": [
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "componentName",
"columnName": "componentName",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "drawable",
"columnName": "drawable",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "iconPack",
"columnName": "iconPack",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "scale",
"columnName": "scale",
"affinity": "REAL",
"notNull": false
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "IconPack",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `version` TEXT NOT NULL, `scale` REAL NOT NULL, PRIMARY KEY(`packageName`))",
"fields": [
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "packageName",
"columnName": "packageName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "scale",
"columnName": "scale",
"affinity": "REAL",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"packageName"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "Widget",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `data` TEXT NOT NULL, `height` INTEGER NOT NULL, `position` INTEGER NOT NULL, `label` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"fields": [
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "data",
"columnName": "data",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "height",
"columnName": "height",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "position",
"columnName": "position",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "label",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "CustomAttributes",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `type` TEXT NOT NULL, `value` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3321ba63cb650a091b1ca102198a41ba')"
]
}
}

View File

@ -18,7 +18,7 @@ import de.mm20.launcher2.database.entities.*
IconEntity::class, IconEntity::class,
IconPackEntity::class, IconPackEntity::class,
WidgetEntity::class, WidgetEntity::class,
CustomAttributeEntity::class], version = 16, exportSchema = true) CustomAttributeEntity::class], version = 17, exportSchema = true)
@TypeConverters(ComponentNameConverter::class, StringListConverter::class) @TypeConverters(ComponentNameConverter::class, StringListConverter::class)
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
@ -61,6 +61,7 @@ abstract class AppDatabase : RoomDatabase() {
Migration_13_14(), Migration_13_14(),
Migration_14_15(), Migration_14_15(),
Migration_15_16(), Migration_15_16(),
Migration_16_17(),
).build() ).build()
if (_instance == null) _instance = instance if (_instance == null) _instance = instance
return instance return instance
@ -166,3 +167,10 @@ class Migration_15_16 : Migration(15, 16) {
""".trimIndent()) """.trimIndent())
} }
} }
class Migration_16_17 : Migration(16, 17) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Websearch ADD COLUMN encoding INTEGER")
}
}

View File

@ -9,5 +9,6 @@ data class WebsearchEntity(
var label: String, var label: String,
var color: Int, var color: Int,
var icon: String?, var icon: String?,
var encoding: Int?,
@PrimaryKey(autoGenerate = true) val id: Long? @PrimaryKey(autoGenerate = true) val id: Long?
) )

View File

@ -208,6 +208,11 @@
<string name="websearch_dialog_custom_icon">Custom icon</string> <string name="websearch_dialog_custom_icon">Custom icon</string>
<string name="websearch_dialog_import_url">Import from URL</string> <string name="websearch_dialog_import_url">Import from URL</string>
<string name="websearch_dialog_import_error">The given URL cannot be imported automatically. You can try a different URL or enter the required data manually.</string> <string name="websearch_dialog_import_error">The given URL cannot be imported automatically. You can try a different URL or enter the required data manually.</string>
<string name="websearch_dialog_advanced">Advanced</string>
<string name="websearch_dialog_query_endcoding">Query encoding</string>
<string name="websearch_dialog_query_endcoding_url">Percent-encoding</string>
<string name="websearch_dialog_query_endcoding_form">application/x-www-form-urlencoded</string>
<string name="websearch_dialog_query_endcoding_none">None</string>
<string name="menu_edit_widgets">Edit widgets</string> <string name="menu_edit_widgets">Edit widgets</string>
<string name="widget_name_weather">Weather</string> <string name="widget_name_weather">Weather</string>
<string name="widget_name_calendar">Calendar</string> <string name="widget_name_calendar">Calendar</string>

View File

@ -244,6 +244,7 @@ internal class WebsearchRepositoryImpl(
"label" to websearch.label, "label" to websearch.label,
"template" to websearch.urlTemplate, "template" to websearch.urlTemplate,
"icon" to icon, "icon" to icon,
"encoding" to websearch.encoding,
) )
) )
} }
@ -289,6 +290,7 @@ internal class WebsearchRepositoryImpl(
color = json.optInt("color", 0), color = json.optInt("color", 0),
label = json.getString("label"), label = json.getString("label"),
icon = iconFile?.absolutePath, icon = iconFile?.absolutePath,
encoding = json.optInt("encoding"),
id = null id = null
) )
websearches.add(entity) websearches.add(entity)

View File

@ -3,41 +3,79 @@ package de.mm20.launcher2.search.data
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import de.mm20.launcher2.database.entities.WebsearchEntity import de.mm20.launcher2.database.entities.WebsearchEntity
import java.net.URLEncoder
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
class Websearch( class Websearch(
var urlTemplate: String, var urlTemplate: String,
var label: String, var label: String,
var color: Int, var color: Int,
var icon: String?, var icon: String?,
var id: Long? = null, var id: Long? = null,
val query: String? = null var encoding: QueryEncoding = QueryEncoding.UrlEncode,
val query: String? = null,
) { ) {
constructor(entity: WebsearchEntity, query: String? = null) : this( constructor(entity: WebsearchEntity, query: String? = null) : this(
urlTemplate = entity.urlTemplate, urlTemplate = entity.urlTemplate,
label = entity.label, label = entity.label,
icon = entity.icon, icon = entity.icon,
color = entity.color, color = entity.color,
id = entity.id, id = entity.id,
query = query query = query,
encoding = QueryEncoding.fromInt(entity.encoding)
) )
fun toDatabaseEntity(): WebsearchEntity { fun toDatabaseEntity(): WebsearchEntity {
return WebsearchEntity( return WebsearchEntity(
urlTemplate = urlTemplate, urlTemplate = urlTemplate,
color = color, color = color,
icon = icon, icon = icon,
label = label, label = label,
id = id id = id,
encoding = encoding.toInt()
) )
} }
fun getLaunchIntent(): Intent? { fun getLaunchIntent(): Intent? {
if(query == null) return null if (query == null) return null
val intent = Intent(Intent.ACTION_VIEW) val intent = Intent(Intent.ACTION_VIEW)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
val url = urlTemplate.replace("\${1}", Uri.encode(query)) val url = urlTemplate.replace("\${1}", encodeQuery(query, encoding))
intent.data = Uri.parse(url) intent.data = Uri.parse(url)
return intent return intent
} }
private fun encodeQuery(query: String, encoding: QueryEncoding): String {
return when(encoding) {
QueryEncoding.UrlEncode -> Uri.encode(query)
QueryEncoding.FormData -> URLEncoder.encode(query, "UTF-8")
QueryEncoding.None -> query
}
}
enum class QueryEncoding {
UrlEncode,
FormData,
None;
fun toInt(): Int {
return when (this) {
UrlEncode -> 0
FormData -> 1
None -> 2
}
}
companion object {
fun fromInt(value: Int?): QueryEncoding {
return when (value) {
1 -> FormData
2 -> None
else -> UrlEncode
}
}
}
}
} }

View File

@ -17,6 +17,7 @@ import androidx.compose.ui.window.Dialog
fun <T> ListPreference( fun <T> ListPreference(
title: String, title: String,
icon: ImageVector? = null, icon: ImageVector? = null,
iconPadding: Boolean = true,
items: List<ListPreferenceItem<T>>, items: List<ListPreferenceItem<T>>,
value: T, value: T,
summary: String? = items.firstOrNull { value == it.value }?.label, summary: String? = items.firstOrNull { value == it.value }?.label,
@ -33,6 +34,7 @@ fun <T> ListPreference(
title = title, title = title,
summary = summary, summary = summary,
icon = icon, icon = icon,
iconPadding = iconPadding,
enabled = enabled, enabled = enabled,
onClick = { onClick = {
showDialog = true showDialog = true

View File

@ -16,6 +16,7 @@ import androidx.compose.ui.unit.dp
fun Preference( fun Preference(
title: String, title: String,
icon: @Composable (() -> Unit), icon: @Composable (() -> Unit),
iconPadding: Boolean = true,
summary: String? = null, summary: String? = null,
onClick: () -> Unit = {}, onClick: () -> Unit = {},
controls: @Composable (() -> Unit)? = null, controls: @Composable (() -> Unit)? = null,
@ -29,13 +30,17 @@ fun Preference(
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
.alpha(if (enabled) 1f else 0.38f), .alpha(if (enabled) 1f else 0.38f),
) { ) {
Box( if (iconPadding) {
modifier = Modifier Box(
.width(56.dp) modifier = Modifier
.padding(start = 4.dp), .width(56.dp)
contentAlignment = Alignment.CenterStart .padding(start = 4.dp),
) { contentAlignment = Alignment.CenterStart
icon() ) {
icon()
}
} else {
Box(modifier = Modifier.size(0.dp))
} }
Column( Column(
modifier = Modifier.weight(1f).padding(vertical = 16.dp) modifier = Modifier.weight(1f).padding(vertical = 16.dp)
@ -63,13 +68,15 @@ fun Preference(
fun Preference( fun Preference(
title: String, title: String,
icon: ImageVector? = null, icon: ImageVector? = null,
iconPadding: Boolean = true,
summary: String? = null, summary: String? = null,
onClick: () -> Unit = {}, onClick: () -> Unit = {},
controls: @Composable (() -> Unit)? = null, controls: @Composable (() -> Unit)? = null,
enabled: Boolean = true enabled: Boolean = true
) { ) {
Preference( Preference(
title, icon = { title,
icon = {
if (icon != null) { if (icon != null) {
Icon( Icon(
imageVector = icon, imageVector = icon,
@ -77,6 +84,6 @@ fun Preference(
tint = MaterialTheme.colorScheme.primary, tint = MaterialTheme.colorScheme.primary,
) )
} }
}, summary, onClick, controls, enabled }, iconPadding, summary, onClick, controls, enabled
) )
} }

View File

@ -3,22 +3,65 @@ 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.* import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.* import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width
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.rememberScrollState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.* import androidx.compose.material.icons.rounded.Add
import androidx.compose.material3.* import androidx.compose.material.icons.rounded.ArrowForward
import androidx.compose.runtime.* import androidx.compose.material.icons.rounded.Check
import androidx.compose.material.icons.rounded.CloudDownload
import androidx.compose.material.icons.rounded.MoreVert
import androidx.compose.material.icons.rounded.Search
import androidx.compose.material.icons.rounded.Tag
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Divider
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier 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.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -28,6 +71,7 @@ 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.BottomSheetDialog
import de.mm20.launcher2.ui.component.preferences.ListPreference
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
@ -146,6 +190,7 @@ fun EditWebsearchDialog(
var label by remember { mutableStateOf(value.label) } var label by remember { mutableStateOf(value.label) }
var showError by remember { mutableStateOf(false) } var showError by remember { mutableStateOf(false) }
var urlTemplate by remember { mutableStateOf(value.urlTemplate) } var urlTemplate by remember { mutableStateOf(value.urlTemplate) }
var encoding by remember { mutableStateOf(value.encoding) }
var color by remember { mutableStateOf(value.color) } var color by remember { mutableStateOf(value.color) }
var icon by remember { mutableStateOf(value.icon) } var icon by remember { mutableStateOf(value.icon) }
@ -226,6 +271,7 @@ fun EditWebsearchDialog(
} }
value.icon = icon value.icon = icon
value.color = color value.color = color
value.encoding = encoding
onValueSaved(value) onValueSaved(value)
} else { } else {
showError = true showError = true
@ -426,6 +472,37 @@ fun EditWebsearchDialog(
text = stringResource(R.string.websearch_dialog_url_description), text = stringResource(R.string.websearch_dialog_url_description),
style = MaterialTheme.typography.labelMedium style = MaterialTheme.typography.labelMedium
) )
var showAdvanced by remember { mutableStateOf(false) }
AnimatedVisibility(!showAdvanced) {
TextButton(
modifier = Modifier.padding(vertical = 16.dp).align(Alignment.End),
onClick = { showAdvanced = true }) {
Text(stringResource(R.string.websearch_dialog_advanced))
}
}
AnimatedVisibility(showAdvanced) {
Column(
modifier = Modifier.padding(top = 16.dp)
) {
Divider()
ListPreference(
title = stringResource(R.string.websearch_dialog_query_endcoding),
items = listOf(
stringResource(R.string.websearch_dialog_query_endcoding_url) to Websearch.QueryEncoding.UrlEncode,
stringResource(R.string.websearch_dialog_query_endcoding_form) to Websearch.QueryEncoding.FormData,
stringResource(R.string.websearch_dialog_query_endcoding_none) to Websearch.QueryEncoding.None,
),
iconPadding = false,
value = encoding,
onValueChanged = {
encoding = it
}
)
}
}
} }
} }
} }