Re-add websearches to search bar

This commit is contained in:
MM20 2022-11-04 19:35:37 +01:00
parent 872f55625f
commit dbc2c6f62e
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
16 changed files with 138 additions and 26 deletions

View File

@ -46,6 +46,8 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun backupDao(): BackupRestoreDao abstract fun backupDao(): BackupRestoreDao
abstract fun customAttrsDao(): CustomAttrsDao abstract fun customAttrsDao(): CustomAttrsDao
abstract fun searchActionDao(): SearchActionDao
companion object { companion object {
private var _instance: AppDatabase? = null private var _instance: AppDatabase? = null
fun getInstance(context: Context): AppDatabase { fun getInstance(context: Context): AppDatabase {

View File

@ -0,0 +1,12 @@
package de.mm20.launcher2.database
import androidx.room.Dao
import androidx.room.Query
import de.mm20.launcher2.database.entities.SearchActionEntity
import kotlinx.coroutines.flow.Flow
@Dao
interface SearchActionDao {
@Query("SELECT * FROM SearchAction ORDER BY position ASC")
fun getSearchActions(): Flow<List<SearchActionEntity>>
}

View File

@ -8,6 +8,7 @@ import de.mm20.launcher2.ktx.jsonObjectOf
import de.mm20.launcher2.searchactions.builders.SearchActionBuilder import de.mm20.launcher2.searchactions.builders.SearchActionBuilder
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONException import org.json.JSONException
@ -15,7 +16,7 @@ import java.io.File
import java.util.UUID import java.util.UUID
interface SearchActionRepository { interface SearchActionRepository {
fun getSearchActionBuilders(filter: TextType?): Flow<List<SearchActionBuilder>> fun getSearchActionBuilders(): Flow<List<SearchActionBuilder>>
suspend fun export(toDir: File) suspend fun export(toDir: File)
suspend fun import(fromDir: File) suspend fun import(fromDir: File)
@ -25,8 +26,9 @@ internal class SearchActionRepositoryImpl(
private val context: Context, private val context: Context,
private val database: AppDatabase private val database: AppDatabase
): SearchActionRepository { ): SearchActionRepository {
override fun getSearchActionBuilders(filter: TextType?): Flow<List<SearchActionBuilder>> { override fun getSearchActionBuilders(): Flow<List<SearchActionBuilder>> {
TODO("Not yet implemented") val dao = database.searchActionDao()
return dao.getSearchActions().map { it.mapNotNull { SearchActionBuilder.from(it) } }
} }
override suspend fun export(toDir: File) = withContext(Dispatchers.IO) { override suspend fun export(toDir: File) = withContext(Dispatchers.IO) {

View File

@ -1,8 +1,14 @@
package de.mm20.launcher2.searchactions package de.mm20.launcher2.searchactions
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import android.os.UserHandle
import android.util.Log import android.util.Log
import android.util.Xml import android.util.Xml
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
@ -30,7 +36,9 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@ -51,6 +59,7 @@ interface SearchActionService {
suspend fun importWebsearch(url: String, iconSize: Int): WebsearchActionBuilder? suspend fun importWebsearch(url: String, iconSize: Int): WebsearchActionBuilder?
suspend fun createIcon(uri: Uri, size: Int): String? suspend fun createIcon(uri: Uri, size: Int): String?
suspend fun getSearchActivities(): List<ResolveInfo>
} }
internal class SearchActionServiceImpl( internal class SearchActionServiceImpl(
@ -80,8 +89,13 @@ internal class SearchActionServiceImpl(
val classificationResult = textClassifier.classify(context, query) val classificationResult = textClassifier.classify(context, query)
val other = repository.getSearchActionBuilders()
emit(builders.mapNotNull { it.build(context, classificationResult) }.toImmutableList()) emitAll(
other.map {
(builders + it).mapNotNull { it.build(context, classificationResult) }.toImmutableList()
}
)
} }
override suspend fun importWebsearch(url: String, iconSize: Int): WebsearchActionBuilder? = override suspend fun importWebsearch(url: String, iconSize: Int): WebsearchActionBuilder? =
@ -205,4 +219,10 @@ internal class SearchActionServiceImpl(
out.close() out.close()
return@withContext file.absolutePath return@withContext file.absolutePath
} }
override suspend fun getSearchActivities(): List<ResolveInfo> {
val packageManager = context.packageManager
val intent = Intent(Intent.ACTION_SEARCH)
return packageManager.queryIntentActivities(intent, 0)
}
} }

View File

@ -13,6 +13,7 @@ data class AppSearchAction(
): SearchAction { ): SearchAction {
override val icon: SearchActionIcon = SearchActionIcon.Search override val icon: SearchActionIcon = SearchActionIcon.Search
override val iconColor: Int = 0 override val iconColor: Int = 0
override val customIcon: String? = null
override fun start(context: Context) { override fun start(context: Context) {
val intent = Intent(Intent.ACTION_SEARCH).apply { val intent = Intent(Intent.ACTION_SEARCH).apply {

View File

@ -13,6 +13,7 @@ data class CallAction(
override val icon: SearchActionIcon = SearchActionIcon.Phone override val icon: SearchActionIcon = SearchActionIcon.Phone
override val iconColor: Int = 0 override val iconColor: Int = 0
override val customIcon: String? = null
override fun start(context: Context) { override fun start(context: Context) {
val intent = Intent(Intent.ACTION_DIAL).apply { val intent = Intent(Intent.ACTION_DIAL).apply {

View File

@ -13,6 +13,7 @@ class CreateContactAction(
) : SearchAction { ) : SearchAction {
override val icon: SearchActionIcon = SearchActionIcon.Contact override val icon: SearchActionIcon = SearchActionIcon.Contact
override val iconColor: Int = 0 override val iconColor: Int = 0
override val customIcon: String? = null
override fun start(context: Context) { override fun start(context: Context) {
val intent = Intent(Intent.ACTION_INSERT).apply { val intent = Intent(Intent.ACTION_INSERT).apply {

View File

@ -11,7 +11,7 @@ data class EmailAction(
) : SearchAction { ) : SearchAction {
override val icon: SearchActionIcon = SearchActionIcon.Email override val icon: SearchActionIcon = SearchActionIcon.Email
override val iconColor: Int = 0 override val iconColor: Int = 0
override val customIcon: String? = null
override fun start(context: Context) { override fun start(context: Context) {
val intent = Intent(Intent.ACTION_SENDTO).apply { val intent = Intent(Intent.ACTION_SENDTO).apply {
type = "*/*" type = "*/*"

View File

@ -11,7 +11,7 @@ data class MessageAction(
): SearchAction { ): SearchAction {
override val icon: SearchActionIcon = SearchActionIcon.Message override val icon: SearchActionIcon = SearchActionIcon.Message
override val iconColor: Int = 0 override val iconColor: Int = 0
override val customIcon: String? = null
override fun start(context: Context) { override fun start(context: Context) {
val intent = Intent(Intent.ACTION_SENDTO).apply { val intent = Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("sms:$number") data = Uri.parse("sms:$number")

View File

@ -8,13 +8,15 @@ import de.mm20.launcher2.ktx.tryStartActivity
data class OpenUrlAction( data class OpenUrlAction(
override val label: String, override val label: String,
val url: String, val url: String,
override val icon: SearchActionIcon = SearchActionIcon.Website,
override val iconColor: Int = 0,
override val customIcon: String? = null,
) : SearchAction { ) : SearchAction {
override val icon: SearchActionIcon = SearchActionIcon.Website
override val iconColor: Int = 0
override fun start(context: Context) { override fun start(context: Context) {
val url = if (url.startsWith("https://") || url.startsWith("http://")) url else "https://$url" val url =
if (url.startsWith("https://") || url.startsWith("http://")) url else "https://$url"
val intent = Intent(Intent.ACTION_VIEW).apply { val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse(url) data = Uri.parse(url)
flags = Intent.FLAG_ACTIVITY_NEW_TASK flags = Intent.FLAG_ACTIVITY_NEW_TASK

View File

@ -16,7 +16,7 @@ data class ScheduleEventAction(
) : SearchAction { ) : SearchAction {
override val icon: SearchActionIcon = SearchActionIcon.Calendar override val icon: SearchActionIcon = SearchActionIcon.Calendar
override val iconColor: Int = 0 override val iconColor: Int = 0
override val customIcon: String? = null
override fun start(context: Context) { override fun start(context: Context) {
val startTime = date.let { val startTime = date.let {

View File

@ -2,24 +2,58 @@ package de.mm20.launcher2.searchactions.actions
import android.content.Context import android.content.Context
import de.mm20.launcher2.search.Searchable import de.mm20.launcher2.search.Searchable
import de.mm20.launcher2.searchactions.builders.WebsearchActionBuilder
interface SearchAction : Searchable { interface SearchAction : Searchable {
val label: String val label: String
val icon: SearchActionIcon val icon: SearchActionIcon
val iconColor: Int val iconColor: Int
val customIcon: String?
fun start(context: Context) fun start(context: Context)
} }
enum class SearchActionIcon(value: Int) { enum class SearchActionIcon {
Search(0), Search,
Custom(1), Custom,
Website(2), Website,
Alarm(3), Alarm,
Timer(4), Timer,
Contact(5), Contact,
Phone(6), Phone,
Email(7), Email,
Message(8), Message,
Calendar(9), Calendar,
Translate(10), Translate;
fun toInt(): Int {
return when (this) {
Search -> 0
Custom -> 1
Website -> 2
Alarm -> 3
Timer -> 4
Contact -> 5
Phone -> 6
Email -> 7
Message -> 8
Calendar -> 9
Translate -> 10
}
}
companion object {
fun fromInt(value: Int?): SearchActionIcon {
return when (value) {
1 -> Custom
2 -> Website
3 -> Alarm
4 -> Timer
5 -> Contact
6 -> Phone
7 -> Email
8 -> Message
9 -> Calendar
10 -> Translate
else -> Search
}
}
}
} }

View File

@ -12,7 +12,7 @@ data class SetAlarmAction(
) : SearchAction { ) : SearchAction {
override val icon: SearchActionIcon = SearchActionIcon.Alarm override val icon: SearchActionIcon = SearchActionIcon.Alarm
override val iconColor: Int = 0 override val iconColor: Int = 0
override val customIcon: String? = null
override fun start(context: Context) { override fun start(context: Context) {
val intent = Intent(AlarmClock.ACTION_SET_ALARM).apply { val intent = Intent(AlarmClock.ACTION_SET_ALARM).apply {
putExtra(AlarmClock.EXTRA_HOUR, time.hour) putExtra(AlarmClock.EXTRA_HOUR, time.hour)

View File

@ -13,7 +13,7 @@ data class TimerAction(
override val icon: SearchActionIcon = SearchActionIcon.Timer override val icon: SearchActionIcon = SearchActionIcon.Timer
override val iconColor: Int = 0 override val iconColor: Int = 0
override val customIcon: String? = null
override fun start(context: Context) { override fun start(context: Context) {
val intent = Intent(AlarmClock.ACTION_SET_TIMER).apply { val intent = Intent(AlarmClock.ACTION_SET_TIMER).apply {
putExtra(AlarmClock.EXTRA_LENGTH, length.seconds.toInt()) putExtra(AlarmClock.EXTRA_LENGTH, length.seconds.toInt())

View File

@ -1,9 +1,42 @@
package de.mm20.launcher2.searchactions.builders package de.mm20.launcher2.searchactions.builders
import android.content.Context import android.content.Context
import de.mm20.launcher2.searchactions.actions.SearchAction import de.mm20.launcher2.database.entities.SearchActionEntity
import de.mm20.launcher2.searchactions.TextClassificationResult import de.mm20.launcher2.searchactions.TextClassificationResult
import de.mm20.launcher2.searchactions.actions.SearchAction
import de.mm20.launcher2.searchactions.actions.SearchActionIcon
import org.json.JSONException
import org.json.JSONObject
interface SearchActionBuilder { interface SearchActionBuilder {
fun build(context: Context, classifiedQuery: TextClassificationResult): SearchAction? fun build(context: Context, classifiedQuery: TextClassificationResult): SearchAction?
}
companion object {
fun from(entity: SearchActionEntity): SearchActionBuilder? {
val options = entity.options?.let {
try {
JSONObject(it)
} catch (_: JSONException) {
null
}
}
when (entity.type) {
"url" -> {
return WebsearchActionBuilder(
label = entity.label ?: "",
urlTemplate = entity.data,
color = entity.color,
icon = SearchActionIcon.fromInt(entity.icon),
customIcon = entity.customIcon,
encoding = WebsearchActionBuilder.QueryEncoding.fromInt(options?.optInt("encoding"))
)
}
"app" -> {
return null
}
else -> return null
}
}
}
}

View File

@ -12,6 +12,7 @@ class WebsearchActionBuilder(
val label: String, val label: String,
val urlTemplate: String, val urlTemplate: String,
val icon: SearchActionIcon = SearchActionIcon.Search, val icon: SearchActionIcon = SearchActionIcon.Search,
val color: Int = 0,
val customIcon: String? = null, val customIcon: String? = null,
val encoding: QueryEncoding = QueryEncoding.UrlEncode, val encoding: QueryEncoding = QueryEncoding.UrlEncode,
) : SearchActionBuilder { ) : SearchActionBuilder {
@ -21,6 +22,9 @@ class WebsearchActionBuilder(
return OpenUrlAction( return OpenUrlAction(
label = label, label = label,
url = url, url = url,
icon = icon,
customIcon = customIcon,
iconColor = color,
) )
} }