Re-add websearches to search bar
This commit is contained in:
parent
872f55625f
commit
dbc2c6f62e
@ -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 {
|
||||||
|
|||||||
@ -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>>
|
||||||
|
}
|
||||||
@ -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) {
|
||||||
|
|||||||
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 = "*/*"
|
||||||
|
|||||||
@ -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")
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -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)
|
||||||
|
|||||||
@ -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())
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user