From 24e25148f0bed4f8e40fc5e1b8ae34295ec4db92 Mon Sep 17 00:00:00 2001
From: MM20 <15646950+MM2-0@users.noreply.github.com>
Date: Sun, 25 Sep 2022 20:59:29 +0200
Subject: [PATCH] Add multiple options for websearch query encoding
Close #174
---
.../de/mm20/launcher2/backup/BackupManager.kt | 2 +-
.../17.json | 450 ++++++++++++++++++
.../de/mm20/launcher2/database/AppDatabase.kt | 10 +-
.../database/entities/WebsearchEntity.kt | 1 +
i18n/src/main/res/values/strings.xml | 5 +
.../launcher2/search/WebsearchRepository.kt | 2 +
.../mm20/launcher2/search/data/Websearch.kt | 76 ++-
.../component/preferences/ListPreference.kt | 2 +
.../ui/component/preferences/Preference.kt | 25 +-
.../websearch/WebSearchSettingsScreen.kt | 91 +++-
10 files changed, 627 insertions(+), 37 deletions(-)
create mode 100644 database/schemas/de.mm20.launcher2.database.AppDatabase/17.json
diff --git a/backup/src/main/java/de/mm20/launcher2/backup/BackupManager.kt b/backup/src/main/java/de/mm20/launcher2/backup/BackupManager.kt
index d4d53c76..ff4acae8 100644
--- a/backup/src/main/java/de/mm20/launcher2/backup/BackupManager.kt
+++ b/backup/src/main/java/de/mm20/launcher2/backup/BackupManager.kt
@@ -185,7 +185,7 @@ class BackupManager(
companion object {
private const val BackupFormatMajor = 1
- private const val BackupFormatMinor = 1
+ private const val BackupFormatMinor = 2
internal const val BackupFormat = "$BackupFormatMajor.$BackupFormatMinor"
}
}
diff --git a/database/schemas/de.mm20.launcher2.database.AppDatabase/17.json b/database/schemas/de.mm20.launcher2.database.AppDatabase/17.json
new file mode 100644
index 00000000..9bd1f109
--- /dev/null
+++ b/database/schemas/de.mm20.launcher2.database.AppDatabase/17.json
@@ -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')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt b/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt
index 59ea79ba..40a99c39 100644
--- a/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt
+++ b/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt
@@ -18,7 +18,7 @@ import de.mm20.launcher2.database.entities.*
IconEntity::class,
IconPackEntity::class,
WidgetEntity::class,
- CustomAttributeEntity::class], version = 16, exportSchema = true)
+ CustomAttributeEntity::class], version = 17, exportSchema = true)
@TypeConverters(ComponentNameConverter::class, StringListConverter::class)
abstract class AppDatabase : RoomDatabase() {
@@ -61,6 +61,7 @@ abstract class AppDatabase : RoomDatabase() {
Migration_13_14(),
Migration_14_15(),
Migration_15_16(),
+ Migration_16_17(),
).build()
if (_instance == null) _instance = instance
return instance
@@ -165,4 +166,11 @@ class Migration_15_16 : Migration(15, 16) {
)
""".trimIndent())
}
+}
+
+class Migration_16_17 : Migration(16, 17) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("ALTER TABLE Websearch ADD COLUMN encoding INTEGER")
+ }
+
}
\ No newline at end of file
diff --git a/database/src/main/java/de/mm20/launcher2/database/entities/WebsearchEntity.kt b/database/src/main/java/de/mm20/launcher2/database/entities/WebsearchEntity.kt
index 5a14a97a..482f2a79 100644
--- a/database/src/main/java/de/mm20/launcher2/database/entities/WebsearchEntity.kt
+++ b/database/src/main/java/de/mm20/launcher2/database/entities/WebsearchEntity.kt
@@ -9,5 +9,6 @@ data class WebsearchEntity(
var label: String,
var color: Int,
var icon: String?,
+ var encoding: Int?,
@PrimaryKey(autoGenerate = true) val id: Long?
)
\ No newline at end of file
diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml
index 1db60712..4c3ce208 100644
--- a/i18n/src/main/res/values/strings.xml
+++ b/i18n/src/main/res/values/strings.xml
@@ -208,6 +208,11 @@
Custom icon
Import from URL
The given URL cannot be imported automatically. You can try a different URL or enter the required data manually.
+ Advanced
+ Query encoding
+ Percent-encoding
+ application/x-www-form-urlencoded
+ None
Edit widgets
Weather
Calendar
diff --git a/search/src/main/java/de/mm20/launcher2/search/WebsearchRepository.kt b/search/src/main/java/de/mm20/launcher2/search/WebsearchRepository.kt
index 8b716411..6f02b060 100644
--- a/search/src/main/java/de/mm20/launcher2/search/WebsearchRepository.kt
+++ b/search/src/main/java/de/mm20/launcher2/search/WebsearchRepository.kt
@@ -244,6 +244,7 @@ internal class WebsearchRepositoryImpl(
"label" to websearch.label,
"template" to websearch.urlTemplate,
"icon" to icon,
+ "encoding" to websearch.encoding,
)
)
}
@@ -289,6 +290,7 @@ internal class WebsearchRepositoryImpl(
color = json.optInt("color", 0),
label = json.getString("label"),
icon = iconFile?.absolutePath,
+ encoding = json.optInt("encoding"),
id = null
)
websearches.add(entity)
diff --git a/search/src/main/java/de/mm20/launcher2/search/data/Websearch.kt b/search/src/main/java/de/mm20/launcher2/search/data/Websearch.kt
index 3a14bfcf..5e44d9de 100644
--- a/search/src/main/java/de/mm20/launcher2/search/data/Websearch.kt
+++ b/search/src/main/java/de/mm20/launcher2/search/data/Websearch.kt
@@ -3,41 +3,79 @@ package de.mm20.launcher2.search.data
import android.content.Intent
import android.net.Uri
import de.mm20.launcher2.database.entities.WebsearchEntity
+import java.net.URLEncoder
+import java.nio.charset.Charset
+import java.nio.charset.StandardCharsets
class Websearch(
- var urlTemplate: String,
- var label: String,
- var color: Int,
- var icon: String?,
- var id: Long? = null,
- val query: String? = null
+ var urlTemplate: String,
+ var label: String,
+ var color: Int,
+ var icon: String?,
+ var id: Long? = null,
+ var encoding: QueryEncoding = QueryEncoding.UrlEncode,
+ val query: String? = null,
) {
constructor(entity: WebsearchEntity, query: String? = null) : this(
- urlTemplate = entity.urlTemplate,
- label = entity.label,
- icon = entity.icon,
- color = entity.color,
- id = entity.id,
- query = query
+ urlTemplate = entity.urlTemplate,
+ label = entity.label,
+ icon = entity.icon,
+ color = entity.color,
+ id = entity.id,
+ query = query,
+ encoding = QueryEncoding.fromInt(entity.encoding)
)
fun toDatabaseEntity(): WebsearchEntity {
return WebsearchEntity(
- urlTemplate = urlTemplate,
- color = color,
- icon = icon,
- label = label,
- id = id
+ urlTemplate = urlTemplate,
+ color = color,
+ icon = icon,
+ label = label,
+ id = id,
+ encoding = encoding.toInt()
)
}
fun getLaunchIntent(): Intent? {
- if(query == null) return null
+ if (query == null) return null
val intent = Intent(Intent.ACTION_VIEW)
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)
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
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/ListPreference.kt b/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/ListPreference.kt
index 8558af7d..9471c1af 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/ListPreference.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/ListPreference.kt
@@ -17,6 +17,7 @@ import androidx.compose.ui.window.Dialog
fun ListPreference(
title: String,
icon: ImageVector? = null,
+ iconPadding: Boolean = true,
items: List>,
value: T,
summary: String? = items.firstOrNull { value == it.value }?.label,
@@ -33,6 +34,7 @@ fun ListPreference(
title = title,
summary = summary,
icon = icon,
+ iconPadding = iconPadding,
enabled = enabled,
onClick = {
showDialog = true
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/Preference.kt b/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/Preference.kt
index d9a4ec5e..f985349a 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/Preference.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/Preference.kt
@@ -16,6 +16,7 @@ import androidx.compose.ui.unit.dp
fun Preference(
title: String,
icon: @Composable (() -> Unit),
+ iconPadding: Boolean = true,
summary: String? = null,
onClick: () -> Unit = {},
controls: @Composable (() -> Unit)? = null,
@@ -29,13 +30,17 @@ fun Preference(
.padding(horizontal = 16.dp)
.alpha(if (enabled) 1f else 0.38f),
) {
- Box(
- modifier = Modifier
- .width(56.dp)
- .padding(start = 4.dp),
- contentAlignment = Alignment.CenterStart
- ) {
- icon()
+ if (iconPadding) {
+ Box(
+ modifier = Modifier
+ .width(56.dp)
+ .padding(start = 4.dp),
+ contentAlignment = Alignment.CenterStart
+ ) {
+ icon()
+ }
+ } else {
+ Box(modifier = Modifier.size(0.dp))
}
Column(
modifier = Modifier.weight(1f).padding(vertical = 16.dp)
@@ -63,13 +68,15 @@ fun Preference(
fun Preference(
title: String,
icon: ImageVector? = null,
+ iconPadding: Boolean = true,
summary: String? = null,
onClick: () -> Unit = {},
controls: @Composable (() -> Unit)? = null,
enabled: Boolean = true
) {
Preference(
- title, icon = {
+ title,
+ icon = {
if (icon != null) {
Icon(
imageVector = icon,
@@ -77,6 +84,6 @@ fun Preference(
tint = MaterialTheme.colorScheme.primary,
)
}
- }, summary, onClick, controls, enabled
+ }, iconPadding, summary, onClick, controls, enabled
)
}
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/websearch/WebSearchSettingsScreen.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/websearch/WebSearchSettingsScreen.kt
index 88f9a1d1..24f9c82b 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/settings/websearch/WebSearchSettingsScreen.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/websearch/WebSearchSettingsScreen.kt
@@ -3,22 +3,65 @@ package de.mm20.launcher2.ui.settings.websearch
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.foundation.*
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.Canvas
+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.items
import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.rounded.*
-import androidx.compose.material3.*
-import androidx.compose.runtime.*
+import androidx.compose.material.icons.rounded.Add
+import androidx.compose.material.icons.rounded.ArrowForward
+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.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.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
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.res.stringResource
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.ui.R
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.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
@@ -146,6 +190,7 @@ fun EditWebsearchDialog(
var label by remember { mutableStateOf(value.label) }
var showError by remember { mutableStateOf(false) }
var urlTemplate by remember { mutableStateOf(value.urlTemplate) }
+ var encoding by remember { mutableStateOf(value.encoding) }
var color by remember { mutableStateOf(value.color) }
var icon by remember { mutableStateOf(value.icon) }
@@ -226,6 +271,7 @@ fun EditWebsearchDialog(
}
value.icon = icon
value.color = color
+ value.encoding = encoding
onValueSaved(value)
} else {
showError = true
@@ -426,6 +472,37 @@ fun EditWebsearchDialog(
text = stringResource(R.string.websearch_dialog_url_description),
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
+ }
+ )
+ }
+ }
}
}
}