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 1e0e1e49..21dfb858 100644 --- a/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt +++ b/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt @@ -46,6 +46,8 @@ abstract class AppDatabase : RoomDatabase() { abstract fun backupDao(): BackupRestoreDao abstract fun customAttrsDao(): CustomAttrsDao + abstract fun searchActionDao(): SearchActionDao + companion object { private var _instance: AppDatabase? = null fun getInstance(context: Context): AppDatabase { diff --git a/database/src/main/java/de/mm20/launcher2/database/SearchActionDao.kt b/database/src/main/java/de/mm20/launcher2/database/SearchActionDao.kt new file mode 100644 index 00000000..c6281ffa --- /dev/null +++ b/database/src/main/java/de/mm20/launcher2/database/SearchActionDao.kt @@ -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> +} \ No newline at end of file diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/SearchActionRepository.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/SearchActionRepository.kt index 7c226123..4ad8c809 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/SearchActionRepository.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/SearchActionRepository.kt @@ -8,6 +8,7 @@ import de.mm20.launcher2.ktx.jsonObjectOf import de.mm20.launcher2.searchactions.builders.SearchActionBuilder import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext import org.json.JSONArray import org.json.JSONException @@ -15,7 +16,7 @@ import java.io.File import java.util.UUID interface SearchActionRepository { - fun getSearchActionBuilders(filter: TextType?): Flow> + fun getSearchActionBuilders(): Flow> suspend fun export(toDir: File) suspend fun import(fromDir: File) @@ -25,8 +26,9 @@ internal class SearchActionRepositoryImpl( private val context: Context, private val database: AppDatabase ): SearchActionRepository { - override fun getSearchActionBuilders(filter: TextType?): Flow> { - TODO("Not yet implemented") + override fun getSearchActionBuilders(): Flow> { + val dao = database.searchActionDao() + return dao.getSearchActions().map { it.mapNotNull { SearchActionBuilder.from(it) } } } override suspend fun export(toDir: File) = withContext(Dispatchers.IO) { diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/SearchActionService.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/SearchActionService.kt index 2a39ff55..3d3b9a2d 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/SearchActionService.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/SearchActionService.kt @@ -1,8 +1,14 @@ package de.mm20.launcher2.searchactions 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.net.Uri +import android.os.UserHandle import android.util.Log import android.util.Xml import androidx.core.graphics.drawable.toBitmap @@ -30,7 +36,9 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import okhttp3.Request @@ -51,6 +59,7 @@ interface SearchActionService { suspend fun importWebsearch(url: String, iconSize: Int): WebsearchActionBuilder? suspend fun createIcon(uri: Uri, size: Int): String? + suspend fun getSearchActivities(): List } internal class SearchActionServiceImpl( @@ -80,8 +89,13 @@ internal class SearchActionServiceImpl( 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? = @@ -205,4 +219,10 @@ internal class SearchActionServiceImpl( out.close() return@withContext file.absolutePath } + + override suspend fun getSearchActivities(): List { + val packageManager = context.packageManager + val intent = Intent(Intent.ACTION_SEARCH) + return packageManager.queryIntentActivities(intent, 0) + } } \ No newline at end of file diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/AppSearchAction.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/AppSearchAction.kt index 46182254..1e7bb252 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/AppSearchAction.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/AppSearchAction.kt @@ -13,6 +13,7 @@ data class AppSearchAction( ): 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 { diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/CallAction.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/CallAction.kt index bc46770d..a67b8110 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/CallAction.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/CallAction.kt @@ -13,6 +13,7 @@ data class CallAction( override val icon: SearchActionIcon = SearchActionIcon.Phone override val iconColor: Int = 0 + override val customIcon: String? = null override fun start(context: Context) { val intent = Intent(Intent.ACTION_DIAL).apply { diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/CreateContactAction.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/CreateContactAction.kt index d9480372..7dba0603 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/CreateContactAction.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/CreateContactAction.kt @@ -13,6 +13,7 @@ class CreateContactAction( ) : SearchAction { override val icon: SearchActionIcon = SearchActionIcon.Contact override val iconColor: Int = 0 + override val customIcon: String? = null override fun start(context: Context) { val intent = Intent(Intent.ACTION_INSERT).apply { diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/EmailAction.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/EmailAction.kt index b6cafba7..005263ab 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/EmailAction.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/EmailAction.kt @@ -11,7 +11,7 @@ data class EmailAction( ) : SearchAction { override val icon: SearchActionIcon = SearchActionIcon.Email override val iconColor: Int = 0 - + override val customIcon: String? = null override fun start(context: Context) { val intent = Intent(Intent.ACTION_SENDTO).apply { type = "*/*" diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/MessageAction.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/MessageAction.kt index 87f7fffe..87fe826f 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/MessageAction.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/MessageAction.kt @@ -11,7 +11,7 @@ data class MessageAction( ): SearchAction { override val icon: SearchActionIcon = SearchActionIcon.Message override val iconColor: Int = 0 - + override val customIcon: String? = null override fun start(context: Context) { val intent = Intent(Intent.ACTION_SENDTO).apply { data = Uri.parse("sms:$number") diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/OpenUrlAction.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/OpenUrlAction.kt index d059c0ef..6e8cf61f 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/OpenUrlAction.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/OpenUrlAction.kt @@ -8,13 +8,15 @@ import de.mm20.launcher2.ktx.tryStartActivity data class OpenUrlAction( override val label: String, val url: String, + override val icon: SearchActionIcon = SearchActionIcon.Website, + override val iconColor: Int = 0, + override val customIcon: String? = null, ) : SearchAction { - override val icon: SearchActionIcon = SearchActionIcon.Website - override val iconColor: Int = 0 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 { data = Uri.parse(url) flags = Intent.FLAG_ACTIVITY_NEW_TASK diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/ScheduleEventAction.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/ScheduleEventAction.kt index 699e038e..eecf1525 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/ScheduleEventAction.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/ScheduleEventAction.kt @@ -16,7 +16,7 @@ data class ScheduleEventAction( ) : SearchAction { override val icon: SearchActionIcon = SearchActionIcon.Calendar override val iconColor: Int = 0 - + override val customIcon: String? = null override fun start(context: Context) { val startTime = date.let { diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/SearchAction.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/SearchAction.kt index c966a249..c5e62d91 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/SearchAction.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/SearchAction.kt @@ -2,24 +2,58 @@ package de.mm20.launcher2.searchactions.actions import android.content.Context import de.mm20.launcher2.search.Searchable +import de.mm20.launcher2.searchactions.builders.WebsearchActionBuilder interface SearchAction : Searchable { val label: String val icon: SearchActionIcon val iconColor: Int + val customIcon: String? fun start(context: Context) } -enum class SearchActionIcon(value: Int) { - Search(0), - Custom(1), - Website(2), - Alarm(3), - Timer(4), - Contact(5), - Phone(6), - Email(7), - Message(8), - Calendar(9), - Translate(10), +enum class SearchActionIcon { + Search, + Custom, + Website, + Alarm, + Timer, + Contact, + Phone, + Email, + Message, + Calendar, + 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 + } + } + } } \ No newline at end of file diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/SetAlarmAction.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/SetAlarmAction.kt index b1f9760f..844df78b 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/SetAlarmAction.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/SetAlarmAction.kt @@ -12,7 +12,7 @@ data class SetAlarmAction( ) : SearchAction { override val icon: SearchActionIcon = SearchActionIcon.Alarm override val iconColor: Int = 0 - + override val customIcon: String? = null override fun start(context: Context) { val intent = Intent(AlarmClock.ACTION_SET_ALARM).apply { putExtra(AlarmClock.EXTRA_HOUR, time.hour) diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/TimerAction.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/TimerAction.kt index 1346b41d..98ad8bc9 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/TimerAction.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/actions/TimerAction.kt @@ -13,7 +13,7 @@ data class TimerAction( override val icon: SearchActionIcon = SearchActionIcon.Timer override val iconColor: Int = 0 - + override val customIcon: String? = null override fun start(context: Context) { val intent = Intent(AlarmClock.ACTION_SET_TIMER).apply { putExtra(AlarmClock.EXTRA_LENGTH, length.seconds.toInt()) diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/builders/SearchActionBuilder.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/builders/SearchActionBuilder.kt index bfc9c3b9..fea14214 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/builders/SearchActionBuilder.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/builders/SearchActionBuilder.kt @@ -1,9 +1,42 @@ package de.mm20.launcher2.searchactions.builders 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.actions.SearchAction +import de.mm20.launcher2.searchactions.actions.SearchActionIcon +import org.json.JSONException +import org.json.JSONObject interface SearchActionBuilder { fun build(context: Context, classifiedQuery: TextClassificationResult): SearchAction? -} \ No newline at end of file + + 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 + } + } + } + +} diff --git a/search-actions/src/main/java/de/mm20/launcher2/searchactions/builders/WebsearchActionBuilder.kt b/search-actions/src/main/java/de/mm20/launcher2/searchactions/builders/WebsearchActionBuilder.kt index d7cca418..ee931389 100644 --- a/search-actions/src/main/java/de/mm20/launcher2/searchactions/builders/WebsearchActionBuilder.kt +++ b/search-actions/src/main/java/de/mm20/launcher2/searchactions/builders/WebsearchActionBuilder.kt @@ -12,6 +12,7 @@ class WebsearchActionBuilder( val label: String, val urlTemplate: String, val icon: SearchActionIcon = SearchActionIcon.Search, + val color: Int = 0, val customIcon: String? = null, val encoding: QueryEncoding = QueryEncoding.UrlEncode, ) : SearchActionBuilder { @@ -21,6 +22,9 @@ class WebsearchActionBuilder( return OpenUrlAction( label = label, url = url, + icon = icon, + customIcon = customIcon, + iconColor = color, ) }