Search actions settings [wip]

This commit is contained in:
MM20
2022-11-17 21:46:20 +01:00
parent ca51056bf4
commit 0697068c0c
16 changed files with 1306 additions and 42 deletions
@@ -1,8 +1,11 @@
package de.mm20.launcher2.searchactions
import android.app.SearchManager
import android.app.SearchableInfo
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.ResolveInfo
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.Uri
import android.util.Log
@@ -14,7 +17,6 @@ import coil.size.Scale
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.searchactions.actions.SearchAction
import de.mm20.launcher2.searchactions.actions.SearchActionIcon
import de.mm20.launcher2.searchactions.builders.CallActionBuilder
import de.mm20.launcher2.searchactions.builders.SearchActionBuilder
import de.mm20.launcher2.searchactions.builders.WebsearchActionBuilder
import kotlinx.collections.immutable.ImmutableList
@@ -47,7 +49,7 @@ interface SearchActionService {
suspend fun importWebsearch(url: String, iconSize: Int): WebsearchActionBuilder?
suspend fun getSearchActivities(): List<ResolveInfo>
suspend fun getSearchActivities(): List<ComponentName>
suspend fun createIcon(uri: Uri, size: Int): String?
}
@@ -100,6 +102,16 @@ internal class SearchActionServiceImpl(
} else {
"https://$url"
}
if (u.contains("${1}")) {
return@withContext WebsearchActionBuilder(
urlTemplate = u,
label = "",
iconColor = 0,
icon = SearchActionIcon.Search,
)
}
val document = Jsoup.parse(URL(u), 5000)
val metaElements =
document.select("link[rel=\"search\"][href][type=\"application/opensearchdescription+xml\"]")
@@ -185,6 +197,7 @@ internal class SearchActionServiceImpl(
label = label ?: "",
icon = if (localIconUrl == null) SearchActionIcon.Search else SearchActionIcon.Custom,
customIcon = localIconUrl,
iconColor = if (localIconUrl == null) 0 else 1,
urlTemplate = urlTemplate ?: ""
)
}
@@ -214,9 +227,19 @@ internal class SearchActionServiceImpl(
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)
override suspend fun getSearchActivities(): List<ComponentName> {
return withContext(Dispatchers.Default) {
val resolveInfos = context.packageManager.queryIntentActivities(
Intent(Intent.ACTION_SEARCH).addCategory(Intent.CATEGORY_DEFAULT), PackageManager.GET_META_DATA,
)
resolveInfos.mapNotNull {
if (!it.activityInfo.exported || !it.activityInfo.enabled) return@mapNotNull null
if (it.activityInfo.permission != null && context.checkSelfPermission(it.activityInfo.permission) != PackageManager.PERMISSION_GRANTED) {
return@mapNotNull null
}
val componentName = ComponentName(it.activityInfo.packageName, it.activityInfo.name)
componentName
}
}
}
}
@@ -10,15 +10,16 @@ data class AppSearchAction(
override val label: String,
val componentName: ComponentName,
val query: String,
override val icon: SearchActionIcon = SearchActionIcon.Custom,
override val iconColor: Int = 1,
override val customIcon: String? = null,
): SearchAction {
override val icon: SearchActionIcon = SearchActionIcon.Search
override val iconColor: Int = 0
override val customIcon: String? = null
override fun start(context: Context) {
val intent = Intent(Intent.ACTION_SEARCH).apply {
component = componentName
putExtra(SearchManager.QUERY, query)
putExtra(SearchManager.USER_QUERY, query)
}
context.tryStartActivity(intent)
}
@@ -0,0 +1,22 @@
package de.mm20.launcher2.searchactions.actions
import android.content.Context
import android.content.Intent
import de.mm20.launcher2.ktx.tryStartActivity
class CustomIntentAction(
override val label: String,
val query: String,
val queryKey: String,
val baseIntent: Intent,
override val icon: SearchActionIcon = SearchActionIcon.Custom,
override val iconColor: Int = 1,
override val customIcon: String? = null,
) : SearchAction {
override fun start(context: Context) {
val intent = Intent(baseIntent).also {
it.putExtra(queryKey, query)
}
context.tryStartActivity(intent)
}
}
@@ -8,10 +8,10 @@ import de.mm20.launcher2.searchactions.actions.AppSearchAction
import de.mm20.launcher2.searchactions.actions.SearchAction
import de.mm20.launcher2.searchactions.actions.SearchActionIcon
class AppSearchActionBuilder(
data class AppSearchActionBuilder(
override val label: String,
val componentName: ComponentName,
override val icon: SearchActionIcon = SearchActionIcon.Search,
override val icon: SearchActionIcon = SearchActionIcon.Custom,
override val iconColor: Int = 0,
override val customIcon: String? = null,
) : CustomizableSearchActionBuilder {
@@ -23,6 +23,9 @@ class AppSearchActionBuilder(
label = label,
componentName = componentName,
query = classifiedQuery.text,
icon = icon,
iconColor = iconColor,
customIcon = customIcon,
)
}
}
@@ -0,0 +1,26 @@
package de.mm20.launcher2.searchactions.builders
import android.content.Context
import android.content.Intent
import de.mm20.launcher2.searchactions.TextClassificationResult
import de.mm20.launcher2.searchactions.actions.CustomIntentAction
import de.mm20.launcher2.searchactions.actions.SearchAction
import de.mm20.launcher2.searchactions.actions.SearchActionIcon
data class CustomIntentActionBuilder(
override val label: String,
val queryKey: String,
val baseIntent: Intent,
override val icon: SearchActionIcon = SearchActionIcon.Custom,
override val iconColor: Int = 1,
override val customIcon: String? = null,
) : CustomizableSearchActionBuilder {
override val key: String
get() = "intent://${baseIntent.toUri(0)}"
override fun build(context: Context, classifiedQuery: TextClassificationResult): SearchAction {
return CustomIntentAction(
label, classifiedQuery.text, queryKey, baseIntent, icon, iconColor, customIcon
)
}
}
@@ -1,6 +1,8 @@
package de.mm20.launcher2.searchactions.builders
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.media.metrics.Event
import de.mm20.launcher2.database.entities.SearchActionEntity
import de.mm20.launcher2.ktx.jsonObjectOf
@@ -42,7 +44,23 @@ interface SearchActionBuilder {
)
}
"app" -> {
return null
return AppSearchActionBuilder(
label = entity.label ?: "",
componentName = ComponentName.unflattenFromString(entity.data ?: return null) ?: return null,
iconColor = entity.color ?: 0,
icon = SearchActionIcon.fromInt(entity.icon),
customIcon = entity.customIcon,
)
}
"intent" -> {
return CustomIntentActionBuilder(
entity.label ?: "",
baseIntent = Intent.parseUri(entity.data, 0),
iconColor = entity.color ?: 0,
icon = SearchActionIcon.fromInt(entity.icon),
customIcon = entity.customIcon,
queryKey = options?.getString("extra")?.takeIf { it.isNotEmpty() } ?: return null
)
}
"call" -> return CallActionBuilder(context)
"message" -> return MessageActionBuilder(context)
@@ -70,7 +88,28 @@ interface SearchActionBuilder {
"encoding" to builder.encoding.toInt()
).toString()
)
//is AppSearchActionBuilder -> null
is AppSearchActionBuilder -> SearchActionEntity(
position = position,
type = "app",
label = builder.label,
data = builder.componentName.flattenToShortString(),
color = builder.iconColor,
icon = builder.icon.toInt(),
customIcon = builder.customIcon,
options = null
)
is CustomIntentActionBuilder -> SearchActionEntity(
position = position,
type = "intent",
label = builder.label,
data = builder.baseIntent.toUri(0),
color = builder.iconColor,
icon = builder.icon.toInt(),
customIcon = builder.customIcon,
options = jsonObjectOf(
"extra" to builder.queryKey
).toString()
)
else -> SearchActionEntity(
position = position,
type = builder.key,
@@ -8,7 +8,7 @@ import de.mm20.launcher2.searchactions.actions.SearchAction
import de.mm20.launcher2.searchactions.actions.SearchActionIcon
import java.net.URLEncoder
class WebsearchActionBuilder(
data class WebsearchActionBuilder(
override val label: String,
val urlTemplate: String,
override val icon: SearchActionIcon = SearchActionIcon.Search,