Rename
- PinnableSearchable -> SavableSearchable, - FavoritesItem -> SavedSearchable, - FavoritesItemEntity -> SavedSearchableEntity add type column to searchable table
This commit is contained in:
parent
7e6a4466ca
commit
f88f71e5ee
@ -7,14 +7,13 @@ import android.content.pm.LauncherApps
|
|||||||
import android.os.Process
|
import android.os.Process
|
||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.search.SearchableDeserializer
|
import de.mm20.launcher2.search.SearchableDeserializer
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
class LauncherAppSerializer : SearchableSerializer {
|
class LauncherAppSerializer : SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String {
|
override fun serialize(searchable: SavableSearchable): String {
|
||||||
searchable as LauncherApp
|
searchable as LauncherApp
|
||||||
val json = JSONObject()
|
val json = JSONObject()
|
||||||
json.put("package", searchable.`package`)
|
json.put("package", searchable.`package`)
|
||||||
@ -28,7 +27,7 @@ class LauncherAppSerializer : SearchableSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LauncherAppDeserializer(val context: Context) : SearchableDeserializer {
|
class LauncherAppDeserializer(val context: Context) : SearchableDeserializer {
|
||||||
override fun deserialize(serialized: String): PinnableSearchable? {
|
override fun deserialize(serialized: String): SavableSearchable? {
|
||||||
val json = JSONObject(serialized)
|
val json = JSONObject(serialized)
|
||||||
val launcherApps = context.getSystemService<LauncherApps>()!!
|
val launcherApps = context.getSystemService<LauncherApps>()!!
|
||||||
val userManager = context.getSystemService<UserManager>()!!
|
val userManager = context.getSystemService<UserManager>()!!
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import de.mm20.launcher2.compat.PackageManagerCompat
|
|||||||
import de.mm20.launcher2.icons.*
|
import de.mm20.launcher2.icons.*
|
||||||
import de.mm20.launcher2.ktx.getSerialNumber
|
import de.mm20.launcher2.ktx.getSerialNumber
|
||||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ data class LauncherApp(
|
|||||||
val version: String?,
|
val version: String?,
|
||||||
internal val userSerialNumber: Long,
|
internal val userSerialNumber: Long,
|
||||||
override val labelOverride: String? = null,
|
override val labelOverride: String? = null,
|
||||||
) : PinnableSearchable {
|
) : SavableSearchable {
|
||||||
|
|
||||||
constructor(context: Context, launcherActivityInfo: LauncherActivityInfo): this(
|
constructor(context: Context, launcherActivityInfo: LauncherActivityInfo): this(
|
||||||
launcherActivityInfo,
|
launcherActivityInfo,
|
||||||
|
|||||||
@ -9,18 +9,17 @@ import android.os.Process
|
|||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import de.mm20.launcher2.ktx.jsonObjectOf
|
import de.mm20.launcher2.ktx.jsonObjectOf
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.SearchableDeserializer
|
import de.mm20.launcher2.search.SearchableDeserializer
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
import de.mm20.launcher2.search.data.LauncherShortcut
|
import de.mm20.launcher2.search.data.LauncherShortcut
|
||||||
import de.mm20.launcher2.search.data.LegacyShortcut
|
import de.mm20.launcher2.search.data.LegacyShortcut
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
|
|
||||||
|
|
||||||
class LauncherShortcutSerializer : SearchableSerializer {
|
class LauncherShortcutSerializer : SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String {
|
override fun serialize(searchable: SavableSearchable): String {
|
||||||
searchable as LauncherShortcut
|
searchable as LauncherShortcut
|
||||||
return jsonObjectOf(
|
return jsonObjectOf(
|
||||||
"packagename" to searchable.launcherShortcut.`package`,
|
"packagename" to searchable.launcherShortcut.`package`,
|
||||||
@ -38,7 +37,7 @@ class LauncherShortcutDeserializer(
|
|||||||
val context: Context
|
val context: Context
|
||||||
) : SearchableDeserializer, KoinComponent {
|
) : SearchableDeserializer, KoinComponent {
|
||||||
|
|
||||||
override fun deserialize(serialized: String): PinnableSearchable? {
|
override fun deserialize(serialized: String): SavableSearchable? {
|
||||||
val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
|
val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
|
||||||
if (!launcherApps.hasShortcutHostPermission()) return null
|
if (!launcherApps.hasShortcutHostPermission()) return null
|
||||||
else {
|
else {
|
||||||
@ -82,7 +81,7 @@ class LauncherShortcutDeserializer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LegacyShortcutSerializer: SearchableSerializer {
|
class LegacyShortcutSerializer: SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String {
|
override fun serialize(searchable: SavableSearchable): String {
|
||||||
searchable as LegacyShortcut
|
searchable as LegacyShortcut
|
||||||
return jsonObjectOf(
|
return jsonObjectOf(
|
||||||
"label" to searchable.label,
|
"label" to searchable.label,
|
||||||
@ -103,7 +102,7 @@ class LegacyShortcutSerializer: SearchableSerializer {
|
|||||||
class LegacyShortcutDeserializer(
|
class LegacyShortcutDeserializer(
|
||||||
val context: Context
|
val context: Context
|
||||||
): SearchableDeserializer {
|
): SearchableDeserializer {
|
||||||
override fun deserialize(serialized: String): PinnableSearchable {
|
override fun deserialize(serialized: String): SavableSearchable {
|
||||||
val json = JSONObject(serialized)
|
val json = JSONObject(serialized)
|
||||||
val label = json.getString("label")
|
val label = json.getString("label")
|
||||||
val intent = Intent.parseUri(json.getString("intent"), 0)
|
val intent = Intent.parseUri(json.getString("intent"), 0)
|
||||||
|
|||||||
@ -7,10 +7,9 @@ import de.mm20.launcher2.appshortcuts.R
|
|||||||
import de.mm20.launcher2.icons.ColorLayer
|
import de.mm20.launcher2.icons.ColorLayer
|
||||||
import de.mm20.launcher2.icons.StaticLauncherIcon
|
import de.mm20.launcher2.icons.StaticLauncherIcon
|
||||||
import de.mm20.launcher2.icons.TintedIconLayer
|
import de.mm20.launcher2.icons.TintedIconLayer
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
|
|
||||||
interface AppShortcut: PinnableSearchable {
|
interface AppShortcut: SavableSearchable {
|
||||||
|
|
||||||
val appName: String?
|
val appName: String?
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import de.mm20.launcher2.appshortcuts.R
|
|||||||
import de.mm20.launcher2.icons.*
|
import de.mm20.launcher2.icons.*
|
||||||
import de.mm20.launcher2.ktx.getSerialNumber
|
import de.mm20.launcher2.ktx.getSerialNumber
|
||||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
|||||||
@ -5,12 +5,10 @@ import android.content.Intent
|
|||||||
import android.content.Intent.ShortcutIconResource
|
import android.content.Intent.ShortcutIconResource
|
||||||
import android.graphics.drawable.AdaptiveIconDrawable
|
import android.graphics.drawable.AdaptiveIconDrawable
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import de.mm20.launcher2.icons.*
|
import de.mm20.launcher2.icons.*
|
||||||
import de.mm20.launcher2.ktx.getDrawableOrNull
|
import de.mm20.launcher2.ktx.getDrawableOrNull
|
||||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||||
import de.mm20.launcher2.ktx.tryStartActivity
|
import de.mm20.launcher2.ktx.tryStartActivity
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
|
||||||
|
|
||||||
data class LegacyShortcut(
|
data class LegacyShortcut(
|
||||||
val intent: Intent,
|
val intent: Intent,
|
||||||
|
|||||||
@ -183,7 +183,7 @@ class BackupManager(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val BackupFormatMajor = 1
|
private const val BackupFormatMajor = 1
|
||||||
private const val BackupFormatMinor = 2
|
private const val BackupFormatMinor = 3
|
||||||
internal const val BackupFormat = "$BackupFormatMajor.$BackupFormatMinor"
|
internal const val BackupFormat = "$BackupFormatMajor.$BackupFormatMinor"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import de.mm20.launcher2.icons.StaticLauncherIcon
|
|||||||
import de.mm20.launcher2.ktx.romanize
|
import de.mm20.launcher2.ktx.romanize
|
||||||
import java.text.Collator
|
import java.text.Collator
|
||||||
|
|
||||||
interface PinnableSearchable : Searchable, Comparable<PinnableSearchable> {
|
interface SavableSearchable : Searchable, Comparable<SavableSearchable> {
|
||||||
|
|
||||||
val domain: String
|
val domain: String
|
||||||
val key: String
|
val key: String
|
||||||
@ -16,7 +16,7 @@ interface PinnableSearchable : Searchable, Comparable<PinnableSearchable> {
|
|||||||
val labelOverride: String?
|
val labelOverride: String?
|
||||||
get() = null
|
get() = null
|
||||||
|
|
||||||
fun overrideLabel(label: String): PinnableSearchable
|
fun overrideLabel(label: String): SavableSearchable
|
||||||
|
|
||||||
fun launch(context: Context, options: Bundle?): Boolean
|
fun launch(context: Context, options: Bundle?): Boolean
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ interface PinnableSearchable : Searchable, Comparable<PinnableSearchable> {
|
|||||||
): LauncherIcon? = null
|
): LauncherIcon? = null
|
||||||
|
|
||||||
|
|
||||||
override fun compareTo(other: PinnableSearchable): Int {
|
override fun compareTo(other: SavableSearchable): Int {
|
||||||
val label1 = labelOverride ?: label
|
val label1 = labelOverride ?: label
|
||||||
val label2 = other.labelOverride ?: other.label
|
val label2 = other.labelOverride ?: other.label
|
||||||
return Collator.getInstance().apply { strength = Collator.SECONDARY }
|
return Collator.getInstance().apply { strength = Collator.SECONDARY }
|
||||||
@ -1,11 +1,11 @@
|
|||||||
package de.mm20.launcher2.search
|
package de.mm20.launcher2.search
|
||||||
|
|
||||||
interface SearchableDeserializer {
|
interface SearchableDeserializer {
|
||||||
fun deserialize(serialized: String): PinnableSearchable?
|
fun deserialize(serialized: String): SavableSearchable?
|
||||||
}
|
}
|
||||||
|
|
||||||
class NullDeserializer: SearchableDeserializer {
|
class NullDeserializer: SearchableDeserializer {
|
||||||
override fun deserialize(serialized: String): PinnableSearchable? {
|
override fun deserialize(serialized: String): SavableSearchable? {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
package de.mm20.launcher2.search
|
package de.mm20.launcher2.search
|
||||||
|
|
||||||
interface SearchableSerializer {
|
interface SearchableSerializer {
|
||||||
fun serialize(searchable: PinnableSearchable): String?
|
fun serialize(searchable: SavableSearchable): String?
|
||||||
val typePrefix: String
|
val typePrefix: String
|
||||||
}
|
}
|
||||||
|
|
||||||
class NullSerializer : SearchableSerializer {
|
class NullSerializer : SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String? {
|
override fun serialize(searchable: SavableSearchable): String? {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,16 +7,15 @@ import android.content.pm.PackageManager
|
|||||||
import android.provider.CalendarContract
|
import android.provider.CalendarContract
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.database.getStringOrNull
|
import androidx.core.database.getStringOrNull
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.SearchableDeserializer
|
import de.mm20.launcher2.search.SearchableDeserializer
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
import de.mm20.launcher2.search.data.CalendarEvent
|
import de.mm20.launcher2.search.data.CalendarEvent
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class CalendarEventSerializer: SearchableSerializer {
|
class CalendarEventSerializer: SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String {
|
override fun serialize(searchable: SavableSearchable): String {
|
||||||
searchable as CalendarEvent
|
searchable as CalendarEvent
|
||||||
val json = JSONObject()
|
val json = JSONObject()
|
||||||
json.put("id", searchable.id)
|
json.put("id", searchable.id)
|
||||||
@ -28,7 +27,7 @@ class CalendarEventSerializer: SearchableSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CalendarEventDeserializer(val context: Context): SearchableDeserializer {
|
class CalendarEventDeserializer(val context: Context): SearchableDeserializer {
|
||||||
override fun deserialize(serialized: String): PinnableSearchable? {
|
override fun deserialize(serialized: String): SavableSearchable? {
|
||||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) return null
|
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) return null
|
||||||
val json = JSONObject(serialized)
|
val json = JSONObject(serialized)
|
||||||
val id = json.getLong("id")
|
val id = json.getLong("id")
|
||||||
|
|||||||
@ -9,8 +9,7 @@ import de.mm20.launcher2.icons.ColorLayer
|
|||||||
import de.mm20.launcher2.icons.StaticLauncherIcon
|
import de.mm20.launcher2.icons.StaticLauncherIcon
|
||||||
import de.mm20.launcher2.icons.TextLayer
|
import de.mm20.launcher2.icons.TextLayer
|
||||||
import de.mm20.launcher2.ktx.tryStartActivity
|
import de.mm20.launcher2.ktx.tryStartActivity
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
data class CalendarEvent(
|
data class CalendarEvent(
|
||||||
@ -25,7 +24,7 @@ data class CalendarEvent(
|
|||||||
val description: String,
|
val description: String,
|
||||||
val calendar: Long,
|
val calendar: Long,
|
||||||
override val labelOverride: String? = null,
|
override val labelOverride: String? = null,
|
||||||
) : PinnableSearchable {
|
) : SavableSearchable {
|
||||||
|
|
||||||
override val domain: String = Domain
|
override val domain: String = Domain
|
||||||
|
|
||||||
|
|||||||
@ -6,15 +6,14 @@ import android.content.pm.PackageManager
|
|||||||
import android.provider.ContactsContract
|
import android.provider.ContactsContract
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import de.mm20.launcher2.ktx.jsonObjectOf
|
import de.mm20.launcher2.ktx.jsonObjectOf
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.SearchableDeserializer
|
import de.mm20.launcher2.search.SearchableDeserializer
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
import de.mm20.launcher2.search.data.Contact
|
import de.mm20.launcher2.search.data.Contact
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
class ContactSerializer : SearchableSerializer {
|
class ContactSerializer : SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String {
|
override fun serialize(searchable: SavableSearchable): String {
|
||||||
searchable as Contact
|
searchable as Contact
|
||||||
return jsonObjectOf(
|
return jsonObjectOf(
|
||||||
"id" to searchable.id
|
"id" to searchable.id
|
||||||
@ -26,7 +25,7 @@ class ContactSerializer : SearchableSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ContactDeserializer(val context: Context) : SearchableDeserializer {
|
class ContactDeserializer(val context: Context) : SearchableDeserializer {
|
||||||
override fun deserialize(serialized: String): PinnableSearchable? {
|
override fun deserialize(serialized: String): SavableSearchable? {
|
||||||
if (ContextCompat.checkSelfPermission(
|
if (ContextCompat.checkSelfPermission(
|
||||||
context,
|
context,
|
||||||
Manifest.permission.READ_CONTACTS
|
Manifest.permission.READ_CONTACTS
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import androidx.core.graphics.drawable.toDrawable
|
|||||||
import de.mm20.launcher2.icons.*
|
import de.mm20.launcher2.icons.*
|
||||||
import de.mm20.launcher2.ktx.asBitmap
|
import de.mm20.launcher2.ktx.asBitmap
|
||||||
import de.mm20.launcher2.ktx.tryStartActivity
|
import de.mm20.launcher2.ktx.tryStartActivity
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
import de.mm20.launcher2.search.Searchable
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -29,7 +29,7 @@ data class Contact(
|
|||||||
val whatsapp: Set<ContactInfo>,
|
val whatsapp: Set<ContactInfo>,
|
||||||
val postals: Set<ContactInfo>,
|
val postals: Set<ContactInfo>,
|
||||||
override val labelOverride: String? = null
|
override val labelOverride: String? = null
|
||||||
) : Searchable, PinnableSearchable {
|
) : Searchable, SavableSearchable {
|
||||||
|
|
||||||
override val domain: String = Domain
|
override val domain: String = Domain
|
||||||
override val key: String
|
override val key: String
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import de.mm20.launcher2.database.AppDatabase
|
|||||||
import de.mm20.launcher2.database.entities.CustomAttributeEntity
|
import de.mm20.launcher2.database.entities.CustomAttributeEntity
|
||||||
import de.mm20.launcher2.favorites.FavoritesRepository
|
import de.mm20.launcher2.favorites.FavoritesRepository
|
||||||
import de.mm20.launcher2.ktx.jsonObjectOf
|
import de.mm20.launcher2.ktx.jsonObjectOf
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
@ -19,24 +19,24 @@ import java.io.File
|
|||||||
|
|
||||||
interface CustomAttributesRepository {
|
interface CustomAttributesRepository {
|
||||||
|
|
||||||
fun search(query: String): Flow<ImmutableList<PinnableSearchable>>
|
fun search(query: String): Flow<ImmutableList<SavableSearchable>>
|
||||||
|
|
||||||
fun getCustomIcon(searchable: PinnableSearchable): Flow<CustomIcon?>
|
fun getCustomIcon(searchable: SavableSearchable): Flow<CustomIcon?>
|
||||||
fun setCustomIcon(searchable: PinnableSearchable, icon: CustomIcon?)
|
fun setCustomIcon(searchable: SavableSearchable, icon: CustomIcon?)
|
||||||
|
|
||||||
fun getCustomLabels(items: List<PinnableSearchable>): Flow<List<CustomLabel>>
|
fun getCustomLabels(items: List<SavableSearchable>): Flow<List<CustomLabel>>
|
||||||
fun setCustomLabel(searchable: PinnableSearchable, label: String)
|
fun setCustomLabel(searchable: SavableSearchable, label: String)
|
||||||
fun clearCustomLabel(searchable: PinnableSearchable)
|
fun clearCustomLabel(searchable: SavableSearchable)
|
||||||
|
|
||||||
fun setTags(searchable: PinnableSearchable, tags: List<String>)
|
fun setTags(searchable: SavableSearchable, tags: List<String>)
|
||||||
fun getTags(searchable: PinnableSearchable): Flow<List<String>>
|
fun getTags(searchable: SavableSearchable): Flow<List<String>>
|
||||||
|
|
||||||
suspend fun export(toDir: File)
|
suspend fun export(toDir: File)
|
||||||
suspend fun import(fromDir: File)
|
suspend fun import(fromDir: File)
|
||||||
|
|
||||||
suspend fun getAllTags(startsWith: String? = null): List<String>
|
suspend fun getAllTags(startsWith: String? = null): List<String>
|
||||||
fun getItemsForTag(tag: String): Flow<List<PinnableSearchable>>
|
fun getItemsForTag(tag: String): Flow<List<SavableSearchable>>
|
||||||
fun addTag(item: PinnableSearchable, tag: String)
|
fun addTag(item: SavableSearchable, tag: String)
|
||||||
suspend fun cleanupDatabase(): Int
|
suspend fun cleanupDatabase(): Int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ internal class CustomAttributesRepositoryImpl(
|
|||||||
) : CustomAttributesRepository {
|
) : CustomAttributesRepository {
|
||||||
private val scope = CoroutineScope(Job() + Dispatchers.Default)
|
private val scope = CoroutineScope(Job() + Dispatchers.Default)
|
||||||
|
|
||||||
override fun getCustomIcon(searchable: PinnableSearchable): Flow<CustomIcon?> {
|
override fun getCustomIcon(searchable: SavableSearchable): Flow<CustomIcon?> {
|
||||||
val dao = appDatabase.customAttrsDao()
|
val dao = appDatabase.customAttrsDao()
|
||||||
return dao.getCustomAttribute(searchable.key, CustomAttributeType.Icon.value)
|
return dao.getCustomAttribute(searchable.key, CustomAttributeType.Icon.value)
|
||||||
.map {
|
.map {
|
||||||
@ -54,7 +54,7 @@ internal class CustomAttributesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setCustomIcon(searchable: PinnableSearchable, icon: CustomIcon?) {
|
override fun setCustomIcon(searchable: SavableSearchable, icon: CustomIcon?) {
|
||||||
val dao = appDatabase.customAttrsDao()
|
val dao = appDatabase.customAttrsDao()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
dao.clearCustomAttribute(searchable.key, CustomAttributeType.Icon.value)
|
dao.clearCustomAttribute(searchable.key, CustomAttributeType.Icon.value)
|
||||||
@ -64,7 +64,7 @@ internal class CustomAttributesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCustomLabels(items: List<PinnableSearchable>): Flow<List<CustomLabel>> {
|
override fun getCustomLabels(items: List<SavableSearchable>): Flow<List<CustomLabel>> {
|
||||||
val dao = appDatabase.customAttrsDao()
|
val dao = appDatabase.customAttrsDao()
|
||||||
return dao.getCustomAttributes(items.map { it.key }, CustomAttributeType.Label.value)
|
return dao.getCustomAttributes(items.map { it.key }, CustomAttributeType.Label.value)
|
||||||
.map { list ->
|
.map { list ->
|
||||||
@ -72,7 +72,7 @@ internal class CustomAttributesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setCustomLabel(searchable: PinnableSearchable, label: String) {
|
override fun setCustomLabel(searchable: SavableSearchable, label: String) {
|
||||||
val dao = appDatabase.customAttrsDao()
|
val dao = appDatabase.customAttrsDao()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
favoritesRepository.save(searchable)
|
favoritesRepository.save(searchable)
|
||||||
@ -88,14 +88,14 @@ internal class CustomAttributesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearCustomLabel(searchable: PinnableSearchable) {
|
override fun clearCustomLabel(searchable: SavableSearchable) {
|
||||||
val dao = appDatabase.customAttrsDao()
|
val dao = appDatabase.customAttrsDao()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
dao.clearCustomAttribute(searchable.key, CustomAttributeType.Label.value)
|
dao.clearCustomAttribute(searchable.key, CustomAttributeType.Label.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setTags(searchable: PinnableSearchable, tags: List<String>) {
|
override fun setTags(searchable: SavableSearchable, tags: List<String>) {
|
||||||
val dao = appDatabase.customAttrsDao()
|
val dao = appDatabase.customAttrsDao()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
favoritesRepository.save(searchable)
|
favoritesRepository.save(searchable)
|
||||||
@ -105,7 +105,7 @@ internal class CustomAttributesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTags(searchable: PinnableSearchable): Flow<List<String>> {
|
override fun getTags(searchable: SavableSearchable): Flow<List<String>> {
|
||||||
val dao = appDatabase.customAttrsDao()
|
val dao = appDatabase.customAttrsDao()
|
||||||
return dao.getCustomAttributes(listOf(searchable.key), CustomAttributeType.Tag.value).map {
|
return dao.getCustomAttributes(listOf(searchable.key), CustomAttributeType.Tag.value).map {
|
||||||
it.map { it.value }
|
it.map { it.value }
|
||||||
@ -121,21 +121,21 @@ internal class CustomAttributesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemsForTag(tag: String): Flow<List<PinnableSearchable>> {
|
override fun getItemsForTag(tag: String): Flow<List<SavableSearchable>> {
|
||||||
val dao = appDatabase.customAttrsDao()
|
val dao = appDatabase.customAttrsDao()
|
||||||
return dao.getItemsWithTag(tag).map {
|
return dao.getItemsWithTag(tag).map {
|
||||||
favoritesRepository.getFromKeys(it)
|
favoritesRepository.getFromKeys(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addTag(item: PinnableSearchable, tag: String) {
|
override fun addTag(item: SavableSearchable, tag: String) {
|
||||||
val dao = appDatabase.customAttrsDao()
|
val dao = appDatabase.customAttrsDao()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
dao.addTag(item.key, tag)
|
dao.addTag(item.key, tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun search(query: String): Flow<ImmutableList<PinnableSearchable>> {
|
override fun search(query: String): Flow<ImmutableList<SavableSearchable>> {
|
||||||
if (query.isBlank()) {
|
if (query.isBlank()) {
|
||||||
return flow {
|
return flow {
|
||||||
emit(persistentListOf())
|
emit(persistentListOf())
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
package de.mm20.launcher2.customattrs.utils
|
package de.mm20.launcher2.customattrs.utils
|
||||||
|
|
||||||
import de.mm20.launcher2.customattrs.CustomAttributesRepository
|
import de.mm20.launcher2.customattrs.CustomAttributesRepository
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.channelFlow
|
import kotlinx.coroutines.flow.channelFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
|
||||||
fun <T: PinnableSearchable>Flow<List<T>>.withCustomLabels(
|
fun <T: SavableSearchable>Flow<List<T>>.withCustomLabels(
|
||||||
customAttributesRepository: CustomAttributesRepository,
|
customAttributesRepository: CustomAttributesRepository,
|
||||||
): Flow<List<T>> = channelFlow {
|
): Flow<List<T>> = channelFlow {
|
||||||
this@withCustomLabels.collectLatest { items ->
|
this@withCustomLabels.collectLatest { items ->
|
||||||
|
|||||||
456
database/schemas/de.mm20.launcher2.database.AppDatabase/18.json
Normal file
456
database/schemas/de.mm20.launcher2.database.AppDatabase/18.json
Normal file
@ -0,0 +1,456 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 18,
|
||||||
|
"identityHash": "4ae30e68d30e179ef6d30b562bbc3011",
|
||||||
|
"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, `type` 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": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"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, '4ae30e68d30e179ef6d30b562bbc3011')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,13 +12,13 @@ import androidx.sqlite.db.SupportSQLiteDatabase
|
|||||||
import de.mm20.launcher2.database.entities.*
|
import de.mm20.launcher2.database.entities.*
|
||||||
|
|
||||||
@Database(entities = [ForecastEntity::class,
|
@Database(entities = [ForecastEntity::class,
|
||||||
FavoritesItemEntity::class,
|
SavedSearchableEntity::class,
|
||||||
WebsearchEntity::class,
|
WebsearchEntity::class,
|
||||||
CurrencyEntity::class,
|
CurrencyEntity::class,
|
||||||
IconEntity::class,
|
IconEntity::class,
|
||||||
IconPackEntity::class,
|
IconPackEntity::class,
|
||||||
WidgetEntity::class,
|
WidgetEntity::class,
|
||||||
CustomAttributeEntity::class], version = 17, exportSchema = true)
|
CustomAttributeEntity::class], version = 18, exportSchema = true)
|
||||||
@TypeConverters(ComponentNameConverter::class, StringListConverter::class)
|
@TypeConverters(ComponentNameConverter::class, StringListConverter::class)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
@ -62,6 +62,7 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
Migration_14_15(),
|
Migration_14_15(),
|
||||||
Migration_15_16(),
|
Migration_15_16(),
|
||||||
Migration_16_17(),
|
Migration_16_17(),
|
||||||
|
Migration_17_18(),
|
||||||
).build()
|
).build()
|
||||||
if (_instance == null) _instance = instance
|
if (_instance == null) _instance = instance
|
||||||
return instance
|
return instance
|
||||||
@ -172,5 +173,15 @@ class Migration_16_17 : Migration(16, 17) {
|
|||||||
override fun migrate(database: SupportSQLiteDatabase) {
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
database.execSQL("ALTER TABLE Websearch ADD COLUMN encoding INTEGER")
|
database.execSQL("ALTER TABLE Websearch ADD COLUMN encoding INTEGER")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Migration_17_18: Migration(17, 18) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("ALTER TABLE Searchable ADD COLUMN type TEXT NOT NULL DEFAULT ''")
|
||||||
|
database.execSQL("""
|
||||||
|
UPDATE Searchable
|
||||||
|
SET type = SUBSTR(`key`, 0, INSTR(`key`, '://')),
|
||||||
|
searchable = SUBSTR(`searchable`, INSTR(`searchable`, '#') + 1)
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -5,7 +5,7 @@ import androidx.room.Insert
|
|||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import de.mm20.launcher2.database.entities.CustomAttributeEntity
|
import de.mm20.launcher2.database.entities.CustomAttributeEntity
|
||||||
import de.mm20.launcher2.database.entities.FavoritesItemEntity
|
import de.mm20.launcher2.database.entities.SavedSearchableEntity
|
||||||
import de.mm20.launcher2.database.entities.WebsearchEntity
|
import de.mm20.launcher2.database.entities.WebsearchEntity
|
||||||
import de.mm20.launcher2.database.entities.WidgetEntity
|
import de.mm20.launcher2.database.entities.WidgetEntity
|
||||||
|
|
||||||
@ -16,10 +16,10 @@ interface BackupRestoreDao {
|
|||||||
suspend fun wipeFavorites()
|
suspend fun wipeFavorites()
|
||||||
|
|
||||||
@Query("SELECT * FROM Searchable LIMIT :limit OFFSET :offset")
|
@Query("SELECT * FROM Searchable LIMIT :limit OFFSET :offset")
|
||||||
suspend fun exportFavorites(limit: Int, offset: Int): List<FavoritesItemEntity>
|
suspend fun exportFavorites(limit: Int, offset: Int): List<SavedSearchableEntity>
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
suspend fun importFavorites(items: List<FavoritesItemEntity>)
|
suspend fun importFavorites(items: List<SavedSearchableEntity>)
|
||||||
|
|
||||||
@Query("DELETE FROM Widget")
|
@Query("DELETE FROM Widget")
|
||||||
suspend fun wipeWidgets()
|
suspend fun wipeWidgets()
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package de.mm20.launcher2.database
|
package de.mm20.launcher2.database
|
||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import de.mm20.launcher2.database.entities.FavoritesItemEntity
|
import de.mm20.launcher2.database.entities.SavedSearchableEntity
|
||||||
import de.mm20.launcher2.database.entities.WebsearchEntity
|
import de.mm20.launcher2.database.entities.WebsearchEntity
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@ -9,16 +9,16 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
interface SearchDao {
|
interface SearchDao {
|
||||||
|
|
||||||
@Insert()
|
@Insert()
|
||||||
fun insertAll(items: List<FavoritesItemEntity>)
|
fun insertAll(items: List<SavedSearchableEntity>)
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
fun insertAllSkipExisting(items: List<FavoritesItemEntity>)
|
fun insertAllSkipExisting(items: List<SavedSearchableEntity>)
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
fun insertSkipExisting(items: FavoritesItemEntity)
|
fun insertSkipExisting(items: SavedSearchableEntity)
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
fun insertAllReplaceExisting(items: List<FavoritesItemEntity>)
|
fun insertAllReplaceExisting(items: List<SavedSearchableEntity>)
|
||||||
|
|
||||||
|
|
||||||
@Query("SELECT * FROM Searchable " +
|
@Query("SELECT * FROM Searchable " +
|
||||||
@ -31,7 +31,7 @@ interface SearchDao {
|
|||||||
automaticallySorted: Boolean = false,
|
automaticallySorted: Boolean = false,
|
||||||
frequentlyUsed: Boolean = false,
|
frequentlyUsed: Boolean = false,
|
||||||
limit: Int,
|
limit: Int,
|
||||||
): Flow<List<FavoritesItemEntity>>
|
): Flow<List<SavedSearchableEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Searchable " +
|
@Query("SELECT * FROM Searchable " +
|
||||||
"WHERE SUBSTR(`key`, 0, INSTR(`key`, '://')) IN (:includeTypes) AND (" +
|
"WHERE SUBSTR(`key`, 0, INSTR(`key`, '://')) IN (:includeTypes) AND (" +
|
||||||
@ -45,10 +45,10 @@ interface SearchDao {
|
|||||||
automaticallySorted: Boolean = false,
|
automaticallySorted: Boolean = false,
|
||||||
frequentlyUsed: Boolean = false,
|
frequentlyUsed: Boolean = false,
|
||||||
limit: Int,
|
limit: Int,
|
||||||
): Flow<List<FavoritesItemEntity>>
|
): Flow<List<SavedSearchableEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Searchable " +
|
@Query("SELECT * FROM Searchable " +
|
||||||
"WHERE SUBSTR(`key`, 0, INSTR(`key`, '://')) NOT IN (:excludeTypes) AND (" +
|
"WHERE `type` NOT IN (:excludeTypes) AND (" +
|
||||||
"(:manuallySorted AND pinned > 1) OR " +
|
"(:manuallySorted AND pinned > 1) OR " +
|
||||||
"(:automaticallySorted AND pinned = 1) OR" +
|
"(:automaticallySorted AND pinned = 1) OR" +
|
||||||
"(:frequentlyUsed AND pinned = 0 AND launchCount > 0)" +
|
"(:frequentlyUsed AND pinned = 0 AND launchCount > 0)" +
|
||||||
@ -59,7 +59,7 @@ interface SearchDao {
|
|||||||
automaticallySorted: Boolean = false,
|
automaticallySorted: Boolean = false,
|
||||||
frequentlyUsed: Boolean = false,
|
frequentlyUsed: Boolean = false,
|
||||||
limit: Int,
|
limit: Int,
|
||||||
): Flow<List<FavoritesItemEntity>>
|
): Flow<List<SavedSearchableEntity>>
|
||||||
|
|
||||||
@Query("SELECT `key` FROM Searchable WHERE hidden = 1 AND `key` LIKE 'calendar://%'")
|
@Query("SELECT `key` FROM Searchable WHERE hidden = 1 AND `key` LIKE 'calendar://%'")
|
||||||
fun getHiddenCalendarEventKeys(): Flow<List<String>>
|
fun getHiddenCalendarEventKeys(): Flow<List<String>>
|
||||||
@ -69,7 +69,7 @@ interface SearchDao {
|
|||||||
fun getPinCount(): Int
|
fun getPinCount(): Int
|
||||||
|
|
||||||
@Query("SELECT * FROM Searchable WHERE pinned = 0 AND launchCount > 0 AND hidden = 0 AND NOT `key` LIKE 'calendar://%' ORDER BY launchCount DESC LIMIT :count")
|
@Query("SELECT * FROM Searchable WHERE pinned = 0 AND launchCount > 0 AND hidden = 0 AND NOT `key` LIKE 'calendar://%' ORDER BY launchCount DESC LIMIT :count")
|
||||||
fun getAutoFavorites(count: Int): List<FavoritesItemEntity>
|
fun getAutoFavorites(count: Int): List<SavedSearchableEntity>
|
||||||
|
|
||||||
@Query("DELETE FROM Searchable WHERE `key` IN (:keys)")
|
@Query("DELETE FROM Searchable WHERE `key` IN (:keys)")
|
||||||
fun deleteAll(keys: List<String>)
|
fun deleteAll(keys: List<String>)
|
||||||
@ -79,7 +79,7 @@ interface SearchDao {
|
|||||||
fun pinExistingItem(key: String)
|
fun pinExistingItem(key: String)
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
fun pinToFavorites(item: FavoritesItemEntity) {
|
fun pinToFavorites(item: SavedSearchableEntity) {
|
||||||
pinExistingItem(item.key)
|
pinExistingItem(item.key)
|
||||||
insertSkipExisting(item)
|
insertSkipExisting(item)
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@ interface SearchDao {
|
|||||||
fun hideExistingItem(key: String)
|
fun hideExistingItem(key: String)
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
fun hideItem(item: FavoritesItemEntity) {
|
fun hideItem(item: SavedSearchableEntity) {
|
||||||
hideExistingItem(item.key)
|
hideExistingItem(item.key)
|
||||||
insertSkipExisting(item)
|
insertSkipExisting(item)
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ interface SearchDao {
|
|||||||
fun getHiddenItemKeys(): Flow<List<String>>
|
fun getHiddenItemKeys(): Flow<List<String>>
|
||||||
|
|
||||||
@Query("SELECT * FROM SEARCHABLE WHERE hidden = 1")
|
@Query("SELECT * FROM SEARCHABLE WHERE hidden = 1")
|
||||||
fun getHiddenItems(): Flow<List<FavoritesItemEntity>>
|
fun getHiddenItems(): Flow<List<SavedSearchableEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Websearch ORDER BY label ASC")
|
@Query("SELECT * FROM Websearch ORDER BY label ASC")
|
||||||
fun getWebSearches(): Flow<List<WebsearchEntity>>
|
fun getWebSearches(): Flow<List<WebsearchEntity>>
|
||||||
@ -135,22 +135,22 @@ interface SearchDao {
|
|||||||
fun incrementExistingLaunchCount(key: String)
|
fun incrementExistingLaunchCount(key: String)
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
fun incrementLaunchCount(item: FavoritesItemEntity) {
|
fun incrementLaunchCount(item: SavedSearchableEntity) {
|
||||||
incrementExistingLaunchCount(item.key)
|
incrementExistingLaunchCount(item.key)
|
||||||
insertSkipExisting(item)
|
insertSkipExisting(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Query("SELECT * FROM Searchable WHERE `key` = :key")
|
@Query("SELECT * FROM Searchable WHERE `key` = :key")
|
||||||
fun getFavorite(key: String): FavoritesItemEntity?
|
fun getFavorite(key: String): SavedSearchableEntity?
|
||||||
|
|
||||||
@Query("SELECT * FROM Searchable WHERE `key` IN (:keys)")
|
@Query("SELECT * FROM Searchable WHERE `key` IN (:keys)")
|
||||||
suspend fun getFromKeys(keys: List<String>): List<FavoritesItemEntity>
|
suspend fun getFromKeys(keys: List<String>): List<SavedSearchableEntity>
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
fun insertReplaceExisting(toDatabaseEntity: FavoritesItemEntity)
|
fun insertReplaceExisting(toDatabaseEntity: SavedSearchableEntity)
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
fun saveFavorites(favorites: List<FavoritesItemEntity>) {
|
fun saveFavorites(favorites: List<SavedSearchableEntity>) {
|
||||||
deleteAllFavorites()
|
deleteAllFavorites()
|
||||||
insertAll(favorites)
|
insertAll(favorites)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,9 @@ import androidx.room.Entity
|
|||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
@Entity(tableName = "Searchable")
|
@Entity(tableName = "Searchable")
|
||||||
data class FavoritesItemEntity(
|
data class SavedSearchableEntity(
|
||||||
@PrimaryKey val key: String,
|
@PrimaryKey val key: String,
|
||||||
|
val type: String,
|
||||||
@ColumnInfo(name = "searchable") val serializedSearchable: String,
|
@ColumnInfo(name = "searchable") val serializedSearchable: String,
|
||||||
var launchCount: Int,
|
var launchCount: Int,
|
||||||
@ColumnInfo(name = "pinned") var pinPosition: Int,
|
@ColumnInfo(name = "pinned") var pinPosition: Int,
|
||||||
@ -4,11 +4,10 @@ import android.content.Context
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||||
import de.mm20.launcher2.database.AppDatabase
|
import de.mm20.launcher2.database.AppDatabase
|
||||||
import de.mm20.launcher2.database.entities.FavoritesItemEntity
|
import de.mm20.launcher2.database.entities.SavedSearchableEntity
|
||||||
import de.mm20.launcher2.ktx.jsonObjectOf
|
import de.mm20.launcher2.ktx.jsonObjectOf
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.SearchableDeserializer
|
import de.mm20.launcher2.search.SearchableDeserializer
|
||||||
import de.mm20.launcher2.search.data.CalendarEvent
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
@ -33,46 +32,46 @@ interface FavoritesRepository {
|
|||||||
automaticallySorted: Boolean = false,
|
automaticallySorted: Boolean = false,
|
||||||
frequentlyUsed: Boolean = false,
|
frequentlyUsed: Boolean = false,
|
||||||
limit: Int = 100
|
limit: Int = 100
|
||||||
): Flow<List<PinnableSearchable>>
|
): Flow<List<SavableSearchable>>
|
||||||
|
|
||||||
|
|
||||||
fun getHiddenCalendarEventKeys(): Flow<List<String>>
|
fun getHiddenCalendarEventKeys(): Flow<List<String>>
|
||||||
fun isPinned(searchable: PinnableSearchable): Flow<Boolean>
|
fun isPinned(searchable: SavableSearchable): Flow<Boolean>
|
||||||
fun pinItem(searchable: PinnableSearchable)
|
fun pinItem(searchable: SavableSearchable)
|
||||||
fun unpinItem(searchable: PinnableSearchable)
|
fun unpinItem(searchable: SavableSearchable)
|
||||||
fun isHidden(searchable: PinnableSearchable): Flow<Boolean>
|
fun isHidden(searchable: SavableSearchable): Flow<Boolean>
|
||||||
fun hideItem(searchable: PinnableSearchable)
|
fun hideItem(searchable: SavableSearchable)
|
||||||
fun unhideItem(searchable: PinnableSearchable)
|
fun unhideItem(searchable: SavableSearchable)
|
||||||
fun incrementLaunchCounter(searchable: PinnableSearchable)
|
fun incrementLaunchCounter(searchable: SavableSearchable)
|
||||||
fun updateFavorites(
|
fun updateFavorites(
|
||||||
manuallySorted: List<PinnableSearchable>,
|
manuallySorted: List<SavableSearchable>,
|
||||||
automaticallySorted: List<PinnableSearchable>,
|
automaticallySorted: List<SavableSearchable>,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getHiddenItems(): Flow<List<PinnableSearchable>>
|
fun getHiddenItems(): Flow<List<SavableSearchable>>
|
||||||
fun getHiddenItemKeys(): Flow<List<String>>
|
fun getHiddenItemKeys(): Flow<List<String>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove this item from the Searchable database
|
* Remove this item from the Searchable database
|
||||||
*/
|
*/
|
||||||
fun remove(searchable: PinnableSearchable)
|
fun remove(searchable: SavableSearchable)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove this item from favorites and reset launch counter
|
* Remove this item from favorites and reset launch counter
|
||||||
*/
|
*/
|
||||||
fun removeFromFavorites(searchable: PinnableSearchable)
|
fun removeFromFavorites(searchable: SavableSearchable)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure that this searchable exists in the Favorites table.
|
* Ensure that this searchable exists in the Favorites table.
|
||||||
* If it doesn't exist, insert it with 0 launch count, not pinned and not hidden
|
* If it doesn't exist, insert it with 0 launch count, not pinned and not hidden
|
||||||
*/
|
*/
|
||||||
fun save(searchable: PinnableSearchable)
|
fun save(searchable: SavableSearchable)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get items with the given keys from the favorites database.
|
* Get items with the given keys from the favorites database.
|
||||||
* Items that don't exist in the database will not be returned.
|
* Items that don't exist in the database will not be returned.
|
||||||
*/
|
*/
|
||||||
suspend fun getFromKeys(keys: List<String>): List<PinnableSearchable>
|
suspend fun getFromKeys(keys: List<String>): List<SavableSearchable>
|
||||||
|
|
||||||
suspend fun export(toDir: File)
|
suspend fun export(toDir: File)
|
||||||
suspend fun import(fromDir: File)
|
suspend fun import(fromDir: File)
|
||||||
@ -99,7 +98,7 @@ internal class FavoritesRepositoryImpl(
|
|||||||
automaticallySorted: Boolean,
|
automaticallySorted: Boolean,
|
||||||
frequentlyUsed: Boolean,
|
frequentlyUsed: Boolean,
|
||||||
limit: Int
|
limit: Int
|
||||||
): Flow<List<PinnableSearchable>> {
|
): Flow<List<SavableSearchable>> {
|
||||||
val dao = database.searchDao()
|
val dao = database.searchDao()
|
||||||
val entities = when {
|
val entities = when {
|
||||||
includeTypes == null && excludeTypes == null -> dao.getFavorites(
|
includeTypes == null && excludeTypes == null -> dao.getFavorites(
|
||||||
@ -137,28 +136,28 @@ internal class FavoritesRepositoryImpl(
|
|||||||
return database.searchDao().getHiddenCalendarEventKeys()
|
return database.searchDao().getHiddenCalendarEventKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isPinned(searchable: PinnableSearchable): Flow<Boolean> {
|
override fun isPinned(searchable: SavableSearchable): Flow<Boolean> {
|
||||||
return AppDatabase.getInstance(context).searchDao().isPinned(searchable.key)
|
return AppDatabase.getInstance(context).searchDao().isPinned(searchable.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pinItem(searchable: PinnableSearchable) {
|
override fun pinItem(searchable: SavableSearchable) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val dao = AppDatabase.getInstance(context).searchDao()
|
val dao = AppDatabase.getInstance(context).searchDao()
|
||||||
val databaseItem = dao.getFavorite(searchable.key)
|
val databaseItem = dao.getFavorite(searchable.key)
|
||||||
val favoritesItem = FavoritesItem(
|
val savedSearchable = SavedSearchable(
|
||||||
key = searchable.key,
|
key = searchable.key,
|
||||||
searchable = searchable,
|
searchable = searchable,
|
||||||
launchCount = databaseItem?.launchCount ?: 0,
|
launchCount = databaseItem?.launchCount ?: 0,
|
||||||
pinPosition = 1,
|
pinPosition = 1,
|
||||||
hidden = false
|
hidden = false
|
||||||
)
|
)
|
||||||
favoritesItem.toDatabaseEntity()?.let { dao.insertReplaceExisting(it) }
|
savedSearchable.toDatabaseEntity()?.let { dao.insertReplaceExisting(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unpinItem(searchable: PinnableSearchable) {
|
override fun unpinItem(searchable: SavableSearchable) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
AppDatabase.getInstance(context).searchDao().unpinFavorite(searchable.key)
|
AppDatabase.getInstance(context).searchDao().unpinFavorite(searchable.key)
|
||||||
@ -166,28 +165,28 @@ internal class FavoritesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isHidden(searchable: PinnableSearchable): Flow<Boolean> {
|
override fun isHidden(searchable: SavableSearchable): Flow<Boolean> {
|
||||||
return AppDatabase.getInstance(context).searchDao().isHidden(searchable.key)
|
return AppDatabase.getInstance(context).searchDao().isHidden(searchable.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hideItem(searchable: PinnableSearchable) {
|
override fun hideItem(searchable: SavableSearchable) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val dao = AppDatabase.getInstance(context).searchDao()
|
val dao = AppDatabase.getInstance(context).searchDao()
|
||||||
val databaseItem = dao.getFavorite(searchable.key)
|
val databaseItem = dao.getFavorite(searchable.key)
|
||||||
val favoritesItem = FavoritesItem(
|
val savedSearchable = SavedSearchable(
|
||||||
key = searchable.key,
|
key = searchable.key,
|
||||||
searchable = searchable,
|
searchable = searchable,
|
||||||
launchCount = databaseItem?.launchCount ?: 0,
|
launchCount = databaseItem?.launchCount ?: 0,
|
||||||
pinPosition = 0,
|
pinPosition = 0,
|
||||||
hidden = true
|
hidden = true
|
||||||
)
|
)
|
||||||
favoritesItem.toDatabaseEntity()?.let { dao.insertReplaceExisting(it) }
|
savedSearchable.toDatabaseEntity()?.let { dao.insertReplaceExisting(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unhideItem(searchable: PinnableSearchable) {
|
override fun unhideItem(searchable: SavableSearchable) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
AppDatabase.getInstance(context).searchDao().unhideItem(searchable.key)
|
AppDatabase.getInstance(context).searchDao().unhideItem(searchable.key)
|
||||||
@ -195,10 +194,10 @@ internal class FavoritesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun incrementLaunchCounter(searchable: PinnableSearchable) {
|
override fun incrementLaunchCounter(searchable: SavableSearchable) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val item = FavoritesItem(searchable.key, searchable, 0, 0, false)
|
val item = SavedSearchable(searchable.key, searchable, 0, 0, false)
|
||||||
item.toDatabaseEntity()?.let {
|
item.toDatabaseEntity()?.let {
|
||||||
AppDatabase.getInstance(context).searchDao()
|
AppDatabase.getInstance(context).searchDao()
|
||||||
.incrementLaunchCount(it)
|
.incrementLaunchCount(it)
|
||||||
@ -207,7 +206,7 @@ internal class FavoritesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getHiddenItems(): Flow<List<PinnableSearchable>> {
|
override fun getHiddenItems(): Flow<List<SavableSearchable>> {
|
||||||
return database.searchDao().getHiddenItems().map {
|
return database.searchDao().getHiddenItems().map {
|
||||||
it.mapNotNull { fromDatabaseEntity(it).searchable }
|
it.mapNotNull { fromDatabaseEntity(it).searchable }
|
||||||
}
|
}
|
||||||
@ -217,7 +216,7 @@ internal class FavoritesRepositoryImpl(
|
|||||||
return database.searchDao().getHiddenItemKeys()
|
return database.searchDao().getHiddenItemKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun remove(searchable: PinnableSearchable) {
|
override fun remove(searchable: SavableSearchable) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
database.searchDao().deleteByKey(searchable.key)
|
database.searchDao().deleteByKey(searchable.key)
|
||||||
@ -225,16 +224,16 @@ internal class FavoritesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeFromFavorites(searchable: PinnableSearchable) {
|
override fun removeFromFavorites(searchable: SavableSearchable) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
database.searchDao().resetPinStatusAndLaunchCounter(searchable.key)
|
database.searchDao().resetPinStatusAndLaunchCounter(searchable.key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun save(searchable: PinnableSearchable) {
|
override fun save(searchable: SavableSearchable) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val entity = FavoritesItem(
|
val entity = SavedSearchable(
|
||||||
key = searchable.key,
|
key = searchable.key,
|
||||||
searchable = searchable,
|
searchable = searchable,
|
||||||
launchCount = 0,
|
launchCount = 0,
|
||||||
@ -247,8 +246,8 @@ internal class FavoritesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun updateFavorites(
|
override fun updateFavorites(
|
||||||
manuallySorted: List<PinnableSearchable>,
|
manuallySorted: List<SavableSearchable>,
|
||||||
automaticallySorted: List<PinnableSearchable>
|
automaticallySorted: List<SavableSearchable>
|
||||||
) {
|
) {
|
||||||
val dao = database.searchDao()
|
val dao = database.searchDao()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
@ -256,7 +255,7 @@ internal class FavoritesRepositoryImpl(
|
|||||||
val keys = manuallySorted.map { it.key } + automaticallySorted.map { it.key }
|
val keys = manuallySorted.map { it.key } + automaticallySorted.map { it.key }
|
||||||
val entities = dao.getFromKeys(keys)
|
val entities = dao.getFromKeys(keys)
|
||||||
val updatedManuallySorted = manuallySorted.mapIndexedNotNull { index, searchable ->
|
val updatedManuallySorted = manuallySorted.mapIndexedNotNull { index, searchable ->
|
||||||
val entity = entities.find { searchable.key == it.key } ?: FavoritesItem(
|
val entity = entities.find { searchable.key == it.key } ?: SavedSearchable(
|
||||||
key = searchable.key,
|
key = searchable.key,
|
||||||
searchable = searchable,
|
searchable = searchable,
|
||||||
launchCount = 0,
|
launchCount = 0,
|
||||||
@ -268,7 +267,7 @@ internal class FavoritesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
val updatedAutomaticallySorted =
|
val updatedAutomaticallySorted =
|
||||||
automaticallySorted.mapIndexedNotNull { index, searchable ->
|
automaticallySorted.mapIndexedNotNull { index, searchable ->
|
||||||
val entity = entities.find { searchable.key == it.key } ?: FavoritesItem(
|
val entity = entities.find { searchable.key == it.key } ?: SavedSearchable(
|
||||||
key = searchable.key,
|
key = searchable.key,
|
||||||
searchable = searchable,
|
searchable = searchable,
|
||||||
launchCount = 0,
|
launchCount = 0,
|
||||||
@ -288,12 +287,12 @@ internal class FavoritesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun fromDatabaseEntity(entity: FavoritesItemEntity): FavoritesItem {
|
private fun fromDatabaseEntity(entity: SavedSearchableEntity): SavedSearchable {
|
||||||
val deserializer: SearchableDeserializer =
|
val deserializer: SearchableDeserializer =
|
||||||
getDeserializer(context, entity.serializedSearchable)
|
getDeserializer(context, entity.type)
|
||||||
val searchable = deserializer.deserialize(entity.serializedSearchable.substringAfter("#"))
|
val searchable = deserializer.deserialize(entity.serializedSearchable)
|
||||||
if (searchable == null) removeInvalidItem(entity.key)
|
if (searchable == null) removeInvalidItem(entity.key)
|
||||||
return FavoritesItem(
|
return SavedSearchable(
|
||||||
key = entity.key,
|
key = entity.key,
|
||||||
searchable = searchable,
|
searchable = searchable,
|
||||||
launchCount = entity.launchCount,
|
launchCount = entity.launchCount,
|
||||||
@ -308,7 +307,7 @@ internal class FavoritesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getFromKeys(keys: List<String>): List<PinnableSearchable> {
|
override suspend fun getFromKeys(keys: List<String>): List<SavableSearchable> {
|
||||||
val dao = database.searchDao()
|
val dao = database.searchDao()
|
||||||
return dao.getFromKeys(keys)
|
return dao.getFromKeys(keys)
|
||||||
.mapNotNull { fromDatabaseEntity(it).searchable }
|
.mapNotNull { fromDatabaseEntity(it).searchable }
|
||||||
@ -324,6 +323,7 @@ internal class FavoritesRepositoryImpl(
|
|||||||
jsonArray.put(
|
jsonArray.put(
|
||||||
jsonObjectOf(
|
jsonObjectOf(
|
||||||
"key" to fav.key,
|
"key" to fav.key,
|
||||||
|
"type" to fav.type,
|
||||||
"hidden" to fav.hidden,
|
"hidden" to fav.hidden,
|
||||||
"launchCount" to fav.launchCount,
|
"launchCount" to fav.launchCount,
|
||||||
"pinPosition" to fav.pinPosition,
|
"pinPosition" to fav.pinPosition,
|
||||||
@ -348,14 +348,15 @@ internal class FavoritesRepositoryImpl(
|
|||||||
fromDir.listFiles { _, name -> name.startsWith("favorites.") } ?: return@withContext
|
fromDir.listFiles { _, name -> name.startsWith("favorites.") } ?: return@withContext
|
||||||
|
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
val favorites = mutableListOf<FavoritesItemEntity>()
|
val favorites = mutableListOf<SavedSearchableEntity>()
|
||||||
try {
|
try {
|
||||||
val jsonArray = JSONArray(file.inputStream().reader().readText())
|
val jsonArray = JSONArray(file.inputStream().reader().readText())
|
||||||
|
|
||||||
for (i in 0 until jsonArray.length()) {
|
for (i in 0 until jsonArray.length()) {
|
||||||
val json = jsonArray.getJSONObject(i)
|
val json = jsonArray.getJSONObject(i)
|
||||||
val entity = FavoritesItemEntity(
|
val entity = SavedSearchableEntity(
|
||||||
key = json.getString("key"),
|
key = json.getString("key"),
|
||||||
|
type = json.optString("type").takeIf { it.isNotEmpty() } ?: continue,
|
||||||
serializedSearchable = json.getString("searchable"),
|
serializedSearchable = json.getString("searchable"),
|
||||||
launchCount = json.getInt("launchCount"),
|
launchCount = json.getInt("launchCount"),
|
||||||
hidden = json.getBoolean("hidden"),
|
hidden = json.getBoolean("hidden"),
|
||||||
|
|||||||
@ -1,30 +1,28 @@
|
|||||||
package de.mm20.launcher2.favorites
|
package de.mm20.launcher2.favorites
|
||||||
|
|
||||||
import de.mm20.launcher2.database.entities.FavoritesItemEntity
|
import de.mm20.launcher2.database.entities.SavedSearchableEntity
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
|
|
||||||
data class FavoritesItem(
|
data class SavedSearchable(
|
||||||
val key: String,
|
val key: String,
|
||||||
/**
|
/**
|
||||||
* null if searchable could not be deserialized (i.e. the app has been uninstalled)
|
* null if searchable could not be deserialized (i.e. the app has been uninstalled)
|
||||||
*/
|
*/
|
||||||
val searchable: PinnableSearchable?,
|
val searchable: SavableSearchable?,
|
||||||
var launchCount: Int,
|
var launchCount: Int,
|
||||||
var pinPosition: Int,
|
var pinPosition: Int,
|
||||||
var hidden: Boolean
|
var hidden: Boolean
|
||||||
) {
|
) {
|
||||||
private val serializer: SearchableSerializer = getSerializer(searchable)
|
fun toDatabaseEntity(): SavedSearchableEntity? {
|
||||||
|
val serializer = getSerializer(searchable)
|
||||||
fun toDatabaseEntity(): FavoritesItemEntity? {
|
|
||||||
val serializer = serializer
|
|
||||||
|
|
||||||
val data = searchable?.let { serializer.serialize(it) } ?: return null
|
val data = searchable?.let { serializer.serialize(it) } ?: return null
|
||||||
|
|
||||||
return FavoritesItemEntity(
|
return SavedSearchableEntity(
|
||||||
key = key,
|
key = key,
|
||||||
serializedSearchable = "${serializer.typePrefix}#${data}",
|
type = searchable.domain,
|
||||||
|
serializedSearchable = data,
|
||||||
hidden = hidden,
|
hidden = hidden,
|
||||||
pinPosition = pinPosition,
|
pinPosition = pinPosition,
|
||||||
launchCount = launchCount
|
launchCount = launchCount
|
||||||
@ -65,45 +65,44 @@ internal fun getSerializer(searchable: Searchable?): SearchableSerializer {
|
|||||||
return NullSerializer()
|
return NullSerializer()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getDeserializer(context: Context, serialized: String): SearchableDeserializer {
|
internal fun getDeserializer(context: Context, type: String): SearchableDeserializer {
|
||||||
val type = serialized.substringBefore("#")
|
if (type == LauncherApp.Domain) {
|
||||||
if (type == "app") {
|
|
||||||
return LauncherAppDeserializer(context)
|
return LauncherAppDeserializer(context)
|
||||||
}
|
}
|
||||||
if (type == "shortcut") {
|
if (type == LauncherShortcut.Domain) {
|
||||||
return LauncherShortcutDeserializer(context)
|
return LauncherShortcutDeserializer(context)
|
||||||
}
|
}
|
||||||
if (type == "legacyshortcut") {
|
if (type == LegacyShortcut.Domain) {
|
||||||
return LegacyShortcutDeserializer(context)
|
return LegacyShortcutDeserializer(context)
|
||||||
}
|
}
|
||||||
if (type == "calendar") {
|
if (type == CalendarEvent.Domain) {
|
||||||
return CalendarEventDeserializer(context)
|
return CalendarEventDeserializer(context)
|
||||||
}
|
}
|
||||||
if (type == "contact") {
|
if (type == Contact.Domain) {
|
||||||
return ContactDeserializer(context)
|
return ContactDeserializer(context)
|
||||||
}
|
}
|
||||||
if (type == "wikipedia") {
|
if (type == Wikipedia.Domain) {
|
||||||
return WikipediaDeserializer(context)
|
return WikipediaDeserializer(context)
|
||||||
}
|
}
|
||||||
if (type == "gdrive") {
|
if (type == GDriveFile.Domain) {
|
||||||
return GDriveFileDeserializer()
|
return GDriveFileDeserializer()
|
||||||
}
|
}
|
||||||
if (type == "onedrive") {
|
if (type == OneDriveFile.Domain) {
|
||||||
return OneDriveFileDeserializer()
|
return OneDriveFileDeserializer()
|
||||||
}
|
}
|
||||||
if (type == "nextcloud") {
|
if (type == NextcloudFile.Domain) {
|
||||||
return NextcloudFileDeserializer()
|
return NextcloudFileDeserializer()
|
||||||
}
|
}
|
||||||
if (type == "owncloud") {
|
if (type == OwncloudFile.Domain) {
|
||||||
return OwncloudFileDeserializer()
|
return OwncloudFileDeserializer()
|
||||||
}
|
}
|
||||||
if (type == "file") {
|
if (type == LocalFile.Domain) {
|
||||||
return LocalFileDeserializer(context)
|
return LocalFileDeserializer(context)
|
||||||
}
|
}
|
||||||
if (type == "website") {
|
if (type == Website.Domain) {
|
||||||
return WebsiteDeserializer()
|
return WebsiteDeserializer()
|
||||||
}
|
}
|
||||||
if (type == "tag") {
|
if (type == Tag.Domain) {
|
||||||
return TagDeserializer()
|
return TagDeserializer()
|
||||||
}
|
}
|
||||||
return NullDeserializer()
|
return NullDeserializer()
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
package de.mm20.launcher2.favorites
|
package de.mm20.launcher2.favorites
|
||||||
|
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.SearchableDeserializer
|
import de.mm20.launcher2.search.SearchableDeserializer
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.search.data.Tag
|
import de.mm20.launcher2.search.data.Tag
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
class TagSerializer: SearchableSerializer {
|
class TagSerializer: SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String {
|
override fun serialize(searchable: SavableSearchable): String {
|
||||||
searchable as Tag
|
searchable as Tag
|
||||||
val json = JSONObject()
|
val json = JSONObject()
|
||||||
json.put("tag", searchable.tag)
|
json.put("tag", searchable.tag)
|
||||||
@ -20,7 +19,7 @@ class TagSerializer: SearchableSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TagDeserializer: SearchableDeserializer {
|
class TagDeserializer: SearchableDeserializer {
|
||||||
override fun deserialize(serialized: String): PinnableSearchable {
|
override fun deserialize(serialized: String): SavableSearchable {
|
||||||
val json = JSONObject(serialized)
|
val json = JSONObject(serialized)
|
||||||
|
|
||||||
return Tag(json.getString("tag"))
|
return Tag(json.getString("tag"))
|
||||||
|
|||||||
@ -5,13 +5,12 @@ import android.os.Bundle
|
|||||||
import de.mm20.launcher2.icons.ColorLayer
|
import de.mm20.launcher2.icons.ColorLayer
|
||||||
import de.mm20.launcher2.icons.StaticLauncherIcon
|
import de.mm20.launcher2.icons.StaticLauncherIcon
|
||||||
import de.mm20.launcher2.icons.TextLayer
|
import de.mm20.launcher2.icons.TextLayer
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
|
|
||||||
data class Tag(
|
data class Tag(
|
||||||
val tag: String,
|
val tag: String,
|
||||||
override val labelOverride: String? = null
|
override val labelOverride: String? = null
|
||||||
): PinnableSearchable {
|
): SavableSearchable {
|
||||||
|
|
||||||
override val domain: String = Domain
|
override val domain: String = Domain
|
||||||
|
|
||||||
@ -23,7 +22,7 @@ data class Tag(
|
|||||||
override fun launch(context: Context, options: Bundle?): Boolean {
|
override fun launch(context: Context, options: Bundle?): Boolean {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
override fun overrideLabel(label: String): PinnableSearchable {
|
override fun overrideLabel(label: String): SavableSearchable {
|
||||||
return this.copy(labelOverride = label)
|
return this.copy(labelOverride = label)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,7 @@ import androidx.core.database.getStringOrNull
|
|||||||
import de.mm20.launcher2.ktx.jsonObjectOf
|
import de.mm20.launcher2.ktx.jsonObjectOf
|
||||||
import de.mm20.launcher2.permissions.PermissionGroup
|
import de.mm20.launcher2.permissions.PermissionGroup
|
||||||
import de.mm20.launcher2.permissions.PermissionsManager
|
import de.mm20.launcher2.permissions.PermissionsManager
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.search.SearchableDeserializer
|
import de.mm20.launcher2.search.SearchableDeserializer
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
import de.mm20.launcher2.search.data.*
|
import de.mm20.launcher2.search.data.*
|
||||||
@ -16,7 +15,7 @@ import org.koin.core.component.KoinComponent
|
|||||||
import org.koin.core.component.get
|
import org.koin.core.component.get
|
||||||
|
|
||||||
class LocalFileSerializer : SearchableSerializer {
|
class LocalFileSerializer : SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String {
|
override fun serialize(searchable: SavableSearchable): String {
|
||||||
searchable as LocalFile
|
searchable as LocalFile
|
||||||
return jsonObjectOf(
|
return jsonObjectOf(
|
||||||
"id" to searchable.id
|
"id" to searchable.id
|
||||||
@ -30,7 +29,7 @@ class LocalFileSerializer : SearchableSerializer {
|
|||||||
class LocalFileDeserializer(
|
class LocalFileDeserializer(
|
||||||
val context: Context
|
val context: Context
|
||||||
) : SearchableDeserializer, KoinComponent {
|
) : SearchableDeserializer, KoinComponent {
|
||||||
override fun deserialize(serialized: String): PinnableSearchable? {
|
override fun deserialize(serialized: String): SavableSearchable? {
|
||||||
val permissionsManager: PermissionsManager = get()
|
val permissionsManager: PermissionsManager = get()
|
||||||
if (!permissionsManager.checkPermissionOnce(
|
if (!permissionsManager.checkPermissionOnce(
|
||||||
PermissionGroup.ExternalStorage
|
PermissionGroup.ExternalStorage
|
||||||
@ -75,7 +74,7 @@ class LocalFileDeserializer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GDriveFileSerializer : SearchableSerializer {
|
class GDriveFileSerializer : SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String {
|
override fun serialize(searchable: SavableSearchable): String {
|
||||||
searchable as GDriveFile
|
searchable as GDriveFile
|
||||||
return jsonObjectOf(
|
return jsonObjectOf(
|
||||||
"id" to searchable.fileId,
|
"id" to searchable.fileId,
|
||||||
@ -104,7 +103,7 @@ class GDriveFileSerializer : SearchableSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GDriveFileDeserializer : SearchableDeserializer {
|
class GDriveFileDeserializer : SearchableDeserializer {
|
||||||
override fun deserialize(serialized: String): PinnableSearchable {
|
override fun deserialize(serialized: String): SavableSearchable {
|
||||||
val json = JSONObject(serialized)
|
val json = JSONObject(serialized)
|
||||||
val id = json.getString("id")
|
val id = json.getString("id")
|
||||||
val label = json.getString("label")
|
val label = json.getString("label")
|
||||||
@ -135,7 +134,7 @@ class GDriveFileDeserializer : SearchableDeserializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class OneDriveFileSerializer : SearchableSerializer {
|
class OneDriveFileSerializer : SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String {
|
override fun serialize(searchable: SavableSearchable): String {
|
||||||
searchable as OneDriveFile
|
searchable as OneDriveFile
|
||||||
return jsonObjectOf(
|
return jsonObjectOf(
|
||||||
"id" to searchable.fileId,
|
"id" to searchable.fileId,
|
||||||
@ -162,7 +161,7 @@ class OneDriveFileSerializer : SearchableSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class OneDriveFileDeserializer : SearchableDeserializer {
|
class OneDriveFileDeserializer : SearchableDeserializer {
|
||||||
override fun deserialize(serialized: String): PinnableSearchable {
|
override fun deserialize(serialized: String): SavableSearchable {
|
||||||
val json = JSONObject(serialized)
|
val json = JSONObject(serialized)
|
||||||
val fileId = json.getString("id")
|
val fileId = json.getString("id")
|
||||||
val label = json.getString("label")
|
val label = json.getString("label")
|
||||||
@ -190,7 +189,7 @@ class OneDriveFileDeserializer : SearchableDeserializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NextcloudFileSerializer : SearchableSerializer {
|
class NextcloudFileSerializer : SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String {
|
override fun serialize(searchable: SavableSearchable): String {
|
||||||
searchable as NextcloudFile
|
searchable as NextcloudFile
|
||||||
return jsonObjectOf(
|
return jsonObjectOf(
|
||||||
"id" to searchable.fileId,
|
"id" to searchable.fileId,
|
||||||
@ -217,7 +216,7 @@ class NextcloudFileSerializer : SearchableSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NextcloudFileDeserializer : SearchableDeserializer {
|
class NextcloudFileDeserializer : SearchableDeserializer {
|
||||||
override fun deserialize(serialized: String): PinnableSearchable {
|
override fun deserialize(serialized: String): SavableSearchable {
|
||||||
val json = JSONObject(serialized)
|
val json = JSONObject(serialized)
|
||||||
val id = json.getLong("id")
|
val id = json.getLong("id")
|
||||||
val label = json.getString("label")
|
val label = json.getString("label")
|
||||||
@ -243,7 +242,7 @@ class NextcloudFileDeserializer : SearchableDeserializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class OwncloudFileSerializer : SearchableSerializer {
|
class OwncloudFileSerializer : SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String {
|
override fun serialize(searchable: SavableSearchable): String {
|
||||||
searchable as OwncloudFile
|
searchable as OwncloudFile
|
||||||
return jsonObjectOf(
|
return jsonObjectOf(
|
||||||
"id" to searchable.fileId,
|
"id" to searchable.fileId,
|
||||||
@ -270,7 +269,7 @@ class OwncloudFileSerializer : SearchableSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class OwncloudFileDeserializer : SearchableDeserializer {
|
class OwncloudFileDeserializer : SearchableDeserializer {
|
||||||
override fun deserialize(serialized: String): PinnableSearchable {
|
override fun deserialize(serialized: String): SavableSearchable {
|
||||||
val json = JSONObject(serialized)
|
val json = JSONObject(serialized)
|
||||||
val id = json.getLong("id")
|
val id = json.getLong("id")
|
||||||
val label = json.getString("label")
|
val label = json.getString("label")
|
||||||
|
|||||||
@ -6,11 +6,10 @@ import de.mm20.launcher2.files.R
|
|||||||
import de.mm20.launcher2.icons.ColorLayer
|
import de.mm20.launcher2.icons.ColorLayer
|
||||||
import de.mm20.launcher2.icons.StaticLauncherIcon
|
import de.mm20.launcher2.icons.StaticLauncherIcon
|
||||||
import de.mm20.launcher2.icons.TintedIconLayer
|
import de.mm20.launcher2.icons.TintedIconLayer
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface File : PinnableSearchable {
|
interface File : SavableSearchable {
|
||||||
val path: String
|
val path: String
|
||||||
val mimeType: String
|
val mimeType: String
|
||||||
val size: Long
|
val size: Long
|
||||||
|
|||||||
@ -20,10 +20,8 @@ import de.mm20.launcher2.icons.*
|
|||||||
import de.mm20.launcher2.ktx.formatToString
|
import de.mm20.launcher2.ktx.formatToString
|
||||||
import de.mm20.launcher2.ktx.tryStartActivity
|
import de.mm20.launcher2.ktx.tryStartActivity
|
||||||
import de.mm20.launcher2.media.ThumbnailUtilsCompat
|
import de.mm20.launcher2.media.ThumbnailUtilsCompat
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.koin.core.component.KoinComponent
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.File as JavaIOFile
|
import java.io.File as JavaIOFile
|
||||||
|
|
||||||
|
|||||||
@ -13,9 +13,8 @@ import de.mm20.launcher2.icons.transformations.LauncherIconTransformation
|
|||||||
import de.mm20.launcher2.icons.transformations.LegacyToAdaptiveTransformation
|
import de.mm20.launcher2.icons.transformations.LegacyToAdaptiveTransformation
|
||||||
import de.mm20.launcher2.icons.transformations.transform
|
import de.mm20.launcher2.icons.transformations.transform
|
||||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.data.LauncherApp
|
import de.mm20.launcher2.search.data.LauncherApp
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@ -99,7 +98,7 @@ class IconRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun getIcon(searchable: PinnableSearchable, size: Int): Flow<LauncherIcon> = channelFlow {
|
fun getIcon(searchable: SavableSearchable, size: Int): Flow<LauncherIcon> = channelFlow {
|
||||||
iconProviders.collectLatest { providers ->
|
iconProviders.collectLatest { providers ->
|
||||||
transformations.collectLatest { transformations ->
|
transformations.collectLatest { transformations ->
|
||||||
customAttributesRepository.getCustomIcon(searchable).collectLatest { customIcon ->
|
customAttributesRepository.getCustomIcon(searchable).collectLatest { customIcon ->
|
||||||
@ -190,7 +189,7 @@ class IconRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getCustomIconSuggestions(
|
suspend fun getCustomIconSuggestions(
|
||||||
searchable: PinnableSearchable,
|
searchable: SavableSearchable,
|
||||||
size: Int
|
size: Int
|
||||||
): List<CustomIconWithPreview> {
|
): List<CustomIconWithPreview> {
|
||||||
val suggestions = mutableListOf<CustomIconWithPreview>()
|
val suggestions = mutableListOf<CustomIconWithPreview>()
|
||||||
@ -302,7 +301,7 @@ class IconRepository(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getUncustomizedDefaultIcon(searchable: PinnableSearchable, size: Int): CustomIconWithPreview? {
|
suspend fun getUncustomizedDefaultIcon(searchable: SavableSearchable, size: Int): CustomIconWithPreview? {
|
||||||
val icon = iconProviders.first().getFirstIcon(searchable, size)
|
val icon = iconProviders.first().getFirstIcon(searchable, size)
|
||||||
?.transform(transformations.first()) ?: return null
|
?.transform(transformations.first()) ?: return null
|
||||||
return CustomIconWithPreview(
|
return CustomIconWithPreview(
|
||||||
@ -339,7 +338,7 @@ class IconRepository(
|
|||||||
return iconPackIcons + themedIcons
|
return iconPackIcons + themedIcons
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCustomIcon(searchable: PinnableSearchable, icon: CustomIcon?) {
|
fun setCustomIcon(searchable: SavableSearchable, icon: CustomIcon?) {
|
||||||
customAttributesRepository.setCustomIcon(searchable, icon)
|
customAttributesRepository.setCustomIcon(searchable, icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,12 +6,11 @@ import android.content.pm.PackageManager
|
|||||||
import de.mm20.launcher2.icons.DynamicCalendarIcon
|
import de.mm20.launcher2.icons.DynamicCalendarIcon
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
import de.mm20.launcher2.ktx.obtainTypedArrayOrNull
|
import de.mm20.launcher2.ktx.obtainTypedArrayOrNull
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.search.data.LauncherApp
|
import de.mm20.launcher2.search.data.LauncherApp
|
||||||
|
|
||||||
class CalendarIconProvider(val context: Context): IconProvider {
|
class CalendarIconProvider(val context: Context): IconProvider {
|
||||||
override suspend fun getIcon(searchable: PinnableSearchable, size: Int): LauncherIcon? {
|
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
|
||||||
if(searchable !is LauncherApp) return null
|
if(searchable !is LauncherApp) return null
|
||||||
val component = ComponentName(searchable.`package`, searchable.activity)
|
val component = ComponentName(searchable.`package`, searchable.activity)
|
||||||
val pm = context.packageManager
|
val pm = context.packageManager
|
||||||
|
|||||||
@ -4,14 +4,13 @@ import android.content.ComponentName
|
|||||||
import de.mm20.launcher2.customattrs.CustomIconPackIcon
|
import de.mm20.launcher2.customattrs.CustomIconPackIcon
|
||||||
import de.mm20.launcher2.icons.IconPackManager
|
import de.mm20.launcher2.icons.IconPackManager
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
|
|
||||||
class CustomIconPackIconProvider(
|
class CustomIconPackIconProvider(
|
||||||
private val customIcon: CustomIconPackIcon,
|
private val customIcon: CustomIconPackIcon,
|
||||||
private val iconPackManager: IconPackManager,
|
private val iconPackManager: IconPackManager,
|
||||||
) : IconProvider {
|
) : IconProvider {
|
||||||
override suspend fun getIcon(searchable: PinnableSearchable, size: Int): LauncherIcon? {
|
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
|
||||||
return iconPackManager.getIcon(
|
return iconPackManager.getIcon(
|
||||||
customIcon.iconPackPackage,
|
customIcon.iconPackPackage,
|
||||||
ComponentName.unflattenFromString(customIcon.iconComponentName) ?: return null
|
ComponentName.unflattenFromString(customIcon.iconComponentName) ?: return null
|
||||||
|
|||||||
@ -3,14 +3,13 @@ package de.mm20.launcher2.icons.providers
|
|||||||
import de.mm20.launcher2.customattrs.CustomThemedIcon
|
import de.mm20.launcher2.customattrs.CustomThemedIcon
|
||||||
import de.mm20.launcher2.icons.IconPackManager
|
import de.mm20.launcher2.icons.IconPackManager
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
|
|
||||||
class CustomThemedIconProvider(
|
class CustomThemedIconProvider(
|
||||||
private val customIcon: CustomThemedIcon,
|
private val customIcon: CustomThemedIcon,
|
||||||
private val iconPackManager: IconPackManager,
|
private val iconPackManager: IconPackManager,
|
||||||
): IconProvider {
|
): IconProvider {
|
||||||
override suspend fun getIcon(searchable: PinnableSearchable, size: Int): LauncherIcon? {
|
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
|
||||||
return iconPackManager.getThemedIcon(customIcon.iconPackageName)
|
return iconPackManager.getThemedIcon(customIcon.iconPackageName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8,12 +8,11 @@ import android.graphics.drawable.LayerDrawable
|
|||||||
import android.graphics.drawable.RotateDrawable
|
import android.graphics.drawable.RotateDrawable
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import de.mm20.launcher2.icons.*
|
import de.mm20.launcher2.icons.*
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.search.data.LauncherApp
|
import de.mm20.launcher2.search.data.LauncherApp
|
||||||
|
|
||||||
class GoogleClockIconProvider(val context: Context) : IconProvider {
|
class GoogleClockIconProvider(val context: Context) : IconProvider {
|
||||||
override suspend fun getIcon(searchable: PinnableSearchable, size: Int): LauncherIcon? {
|
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
|
||||||
if (searchable !is LauncherApp) return null
|
if (searchable !is LauncherApp) return null
|
||||||
if (searchable.`package` != "com.google.android.deskclock") return null
|
if (searchable.`package` != "com.google.android.deskclock") return null
|
||||||
val pm = context.packageManager
|
val pm = context.packageManager
|
||||||
|
|||||||
@ -3,9 +3,8 @@ package de.mm20.launcher2.icons.providers
|
|||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import de.mm20.launcher2.icons.*
|
import de.mm20.launcher2.icons.*
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.data.LauncherApp
|
import de.mm20.launcher2.search.data.LauncherApp
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@ -14,7 +13,7 @@ class IconPackIconProvider(
|
|||||||
private val iconPack: String,
|
private val iconPack: String,
|
||||||
private val iconPackManager: IconPackManager,
|
private val iconPackManager: IconPackManager,
|
||||||
): IconProvider {
|
): IconProvider {
|
||||||
override suspend fun getIcon(searchable: PinnableSearchable, size: Int): LauncherIcon? {
|
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
|
||||||
if (searchable !is LauncherApp) return null
|
if (searchable !is LauncherApp) return null
|
||||||
|
|
||||||
val component = ComponentName(searchable.`package`, searchable.activity)
|
val component = ComponentName(searchable.`package`, searchable.activity)
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
package de.mm20.launcher2.icons.providers
|
package de.mm20.launcher2.icons.providers
|
||||||
|
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
|
|
||||||
interface IconProvider {
|
interface IconProvider {
|
||||||
suspend fun getIcon(searchable: PinnableSearchable, size: Int): LauncherIcon?
|
suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon?
|
||||||
}
|
}
|
||||||
|
|
||||||
internal suspend fun Iterable<IconProvider>.getFirstIcon(
|
internal suspend fun Iterable<IconProvider>.getFirstIcon(
|
||||||
searchable: PinnableSearchable,
|
searchable: SavableSearchable,
|
||||||
size: Int
|
size: Int
|
||||||
): LauncherIcon? {
|
): LauncherIcon? {
|
||||||
for (provider in this) {
|
for (provider in this) {
|
||||||
|
|||||||
@ -2,11 +2,10 @@ package de.mm20.launcher2.icons.providers
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
|
|
||||||
class PlaceholderIconProvider(val context: Context) : IconProvider {
|
class PlaceholderIconProvider(val context: Context) : IconProvider {
|
||||||
override suspend fun getIcon(searchable: PinnableSearchable, size: Int): LauncherIcon {
|
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon {
|
||||||
return searchable.getPlaceholderIcon(context)
|
return searchable.getPlaceholderIcon(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,14 +2,13 @@ package de.mm20.launcher2.icons.providers
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
|
|
||||||
class SystemIconProvider(
|
class SystemIconProvider(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val themedIcons: Boolean,
|
private val themedIcons: Boolean,
|
||||||
) : IconProvider {
|
) : IconProvider {
|
||||||
override suspend fun getIcon(searchable: PinnableSearchable, size: Int): LauncherIcon? {
|
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
|
||||||
return searchable.loadIcon(context, size, themedIcons)
|
return searchable.loadIcon(context, size, themedIcons)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,15 +1,14 @@
|
|||||||
package de.mm20.launcher2.icons.providers
|
package de.mm20.launcher2.icons.providers
|
||||||
|
|
||||||
import de.mm20.launcher2.icons.*
|
import de.mm20.launcher2.icons.*
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.search.data.LauncherApp
|
import de.mm20.launcher2.search.data.LauncherApp
|
||||||
|
|
||||||
internal class ThemedIconProvider(
|
internal class ThemedIconProvider(
|
||||||
private val iconPackManager: IconPackManager,
|
private val iconPackManager: IconPackManager,
|
||||||
) : IconProvider {
|
) : IconProvider {
|
||||||
|
|
||||||
override suspend fun getIcon(searchable: PinnableSearchable, size: Int): LauncherIcon? {
|
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
|
||||||
if (searchable !is LauncherApp) return null
|
if (searchable !is LauncherApp) return null
|
||||||
return iconPackManager.getThemedIcon(searchable.`package`)
|
return iconPackManager.getThemedIcon(searchable.`package`)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,14 +2,13 @@ package de.mm20.launcher2.icons.providers
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import de.mm20.launcher2.icons.*
|
import de.mm20.launcher2.icons.*
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
|
|
||||||
internal class ThemedPlaceholderIconProvider(
|
internal class ThemedPlaceholderIconProvider(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
) : IconProvider {
|
) : IconProvider {
|
||||||
|
|
||||||
override suspend fun getIcon(searchable: PinnableSearchable, size: Int): LauncherIcon {
|
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon {
|
||||||
val icon = searchable.getPlaceholderIcon(context)
|
val icon = searchable.getPlaceholderIcon(context)
|
||||||
|
|
||||||
return StaticLauncherIcon(
|
return StaticLauncherIcon(
|
||||||
|
|||||||
@ -36,7 +36,6 @@ import de.mm20.launcher2.wikipedia.WikipediaRepository
|
|||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.coroutineScope
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.channelFlow
|
import kotlinx.coroutines.flow.channelFlow
|
||||||
@ -216,7 +215,7 @@ internal class SearchServiceImpl(
|
|||||||
}
|
}
|
||||||
launch {
|
launch {
|
||||||
results
|
results
|
||||||
.map { it.toList().sortedBy { it as? PinnableSearchable }.toImmutableList() }
|
.map { it.toList().sortedBy { it as? SavableSearchable }.toImmutableList() }
|
||||||
.collectLatest {
|
.collectLatest {
|
||||||
send(it)
|
send(it)
|
||||||
}
|
}
|
||||||
@ -235,7 +234,7 @@ internal data class SearchResults(
|
|||||||
val unitConverters: List<UnitConverter> = emptyList(),
|
val unitConverters: List<UnitConverter> = emptyList(),
|
||||||
val websites: List<Website> = emptyList(),
|
val websites: List<Website> = emptyList(),
|
||||||
val wikipedia: List<Wikipedia> = emptyList(),
|
val wikipedia: List<Wikipedia> = emptyList(),
|
||||||
val other: List<PinnableSearchable> = emptyList(),
|
val other: List<SavableSearchable> = emptyList(),
|
||||||
) {
|
) {
|
||||||
fun toList(): List<Searchable> {
|
fun toList(): List<Searchable> {
|
||||||
return (apps + shortcuts + contacts + calendars + websites + wikipedia + other).distinctBy { it.key } + calculators+ unitConverters
|
return (apps + shortcuts + contacts + calendars + websites + wikipedia + other).distinctBy { it.key } + calculators+ unitConverters
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import de.mm20.launcher2.customattrs.CustomAttributesRepository
|
|||||||
import de.mm20.launcher2.customattrs.utils.withCustomLabels
|
import de.mm20.launcher2.customattrs.utils.withCustomLabels
|
||||||
import de.mm20.launcher2.favorites.FavoritesRepository
|
import de.mm20.launcher2.favorites.FavoritesRepository
|
||||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.data.Tag
|
import de.mm20.launcher2.search.data.Tag
|
||||||
import de.mm20.launcher2.widgets.WidgetRepository
|
import de.mm20.launcher2.widgets.WidgetRepository
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
@ -32,7 +32,7 @@ open class FavoritesVM : ViewModel(), KoinComponent {
|
|||||||
it.filterIsInstance<Tag>()
|
it.filterIsInstance<Tag>()
|
||||||
}
|
}
|
||||||
|
|
||||||
val favorites: Flow<List<PinnableSearchable>> = selectedTag.flatMapLatest { tag ->
|
val favorites: Flow<List<SavableSearchable>> = selectedTag.flatMapLatest { tag ->
|
||||||
if (tag == null) {
|
if (tag == null) {
|
||||||
val columns = dataStore.data.map { it.grid.columnCount }
|
val columns = dataStore.data.map { it.grid.columnCount }
|
||||||
val excludeCalendar = widgetRepository.isCalendarWidgetEnabled()
|
val excludeCalendar = widgetRepository.isCalendarWidgetEnabled()
|
||||||
|
|||||||
@ -89,8 +89,7 @@ import androidx.compose.ui.unit.toSize
|
|||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.badges.Badge
|
import de.mm20.launcher2.badges.Badge
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||||
@ -739,7 +738,7 @@ fun ShortcutPicker(viewModel: EditFavoritesSheetVM) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sealed interface FavoritesSheetGridItem {
|
sealed interface FavoritesSheetGridItem {
|
||||||
class Favorite(val item: PinnableSearchable) : FavoritesSheetGridItem
|
class Favorite(val item: SavableSearchable) : FavoritesSheetGridItem
|
||||||
class Divider(val section: FavoritesSheetSection) : FavoritesSheetGridItem
|
class Divider(val section: FavoritesSheetSection) : FavoritesSheetGridItem
|
||||||
class Spacer(val span: Int = 1) : FavoritesSheetGridItem
|
class Spacer(val span: Int = 1) : FavoritesSheetGridItem
|
||||||
object EmptySection : FavoritesSheetGridItem
|
object EmptySection : FavoritesSheetGridItem
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import de.mm20.launcher2.ktx.normalize
|
|||||||
import de.mm20.launcher2.permissions.PermissionGroup
|
import de.mm20.launcher2.permissions.PermissionGroup
|
||||||
import de.mm20.launcher2.permissions.PermissionsManager
|
import de.mm20.launcher2.permissions.PermissionsManager
|
||||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.data.AppShortcut
|
import de.mm20.launcher2.search.data.AppShortcut
|
||||||
import de.mm20.launcher2.search.Searchable
|
import de.mm20.launcher2.search.Searchable
|
||||||
import de.mm20.launcher2.search.data.Tag
|
import de.mm20.launcher2.search.data.Tag
|
||||||
@ -48,9 +48,9 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent {
|
|||||||
|
|
||||||
val createShortcutTarget = MutableLiveData<FavoritesSheetSection?>(null)
|
val createShortcutTarget = MutableLiveData<FavoritesSheetSection?>(null)
|
||||||
|
|
||||||
private var manuallySorted: MutableList<PinnableSearchable> = mutableListOf()
|
private var manuallySorted: MutableList<SavableSearchable> = mutableListOf()
|
||||||
private var automaticallySorted: MutableList<PinnableSearchable> = mutableListOf()
|
private var automaticallySorted: MutableList<SavableSearchable> = mutableListOf()
|
||||||
private var frequentlyUsed: MutableList<PinnableSearchable> = mutableListOf()
|
private var frequentlyUsed: MutableList<SavableSearchable> = mutableListOf()
|
||||||
|
|
||||||
val pinnedTags = MutableLiveData<List<Tag>>(emptyList())
|
val pinnedTags = MutableLiveData<List<Tag>>(emptyList())
|
||||||
val availableTags = MutableLiveData<List<Tag>>(emptyList())
|
val availableTags = MutableLiveData<List<Tag>>(emptyList())
|
||||||
@ -179,7 +179,7 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIcon(searchable: PinnableSearchable, size: Int): Flow<LauncherIcon?> {
|
fun getIcon(searchable: SavableSearchable, size: Int): Flow<LauncherIcon?> {
|
||||||
return iconRepository.getIcon(searchable, size)
|
return iconRepository.getIcon(searchable, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,15 +16,14 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||||
import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid
|
import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HiddenItemsSheet(
|
fun HiddenItemsSheet(
|
||||||
items: List<PinnableSearchable>,
|
items: List<SavableSearchable>,
|
||||||
onDismiss: () -> Unit
|
onDismiss: () -> Unit
|
||||||
) {
|
) {
|
||||||
val viewModel: HiddenItemsSheetVM = viewModel()
|
val viewModel: HiddenItemsSheetVM = viewModel()
|
||||||
|
|||||||
@ -43,8 +43,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.LauncherCard
|
import de.mm20.launcher2.ui.component.LauncherCard
|
||||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||||
@ -378,7 +377,7 @@ fun SearchColumn(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun LazyListScope.GridResults(
|
fun LazyListScope.GridResults(
|
||||||
items: ImmutableList<PinnableSearchable>,
|
items: ImmutableList<SavableSearchable>,
|
||||||
columns: Int,
|
columns: Int,
|
||||||
reverse: Boolean,
|
reverse: Boolean,
|
||||||
showLabels: Boolean,
|
showLabels: Boolean,
|
||||||
@ -443,7 +442,7 @@ fun LazyListScope.GridResults(
|
|||||||
@Composable
|
@Composable
|
||||||
fun GridRow(
|
fun GridRow(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
items: ImmutableList<PinnableSearchable>,
|
items: ImmutableList<SavableSearchable>,
|
||||||
columns: Int,
|
columns: Int,
|
||||||
showLabels: Boolean,
|
showLabels: Boolean,
|
||||||
) {
|
) {
|
||||||
@ -467,7 +466,7 @@ fun GridRow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun LazyListScope.ListResults(
|
fun LazyListScope.ListResults(
|
||||||
items: ImmutableList<PinnableSearchable>,
|
items: ImmutableList<SavableSearchable>,
|
||||||
reverse: Boolean,
|
reverse: Boolean,
|
||||||
key: String,
|
key: String,
|
||||||
before: (@Composable () -> Unit)? = null,
|
before: (@Composable () -> Unit)? = null,
|
||||||
@ -520,7 +519,7 @@ fun LazyListScope.ListResults(
|
|||||||
@Composable
|
@Composable
|
||||||
fun ListRow(
|
fun ListRow(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
item: PinnableSearchable,
|
item: SavableSearchable,
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.padding(
|
modifier = modifier.padding(
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
package de.mm20.launcher2.ui.launcher.search
|
package de.mm20.launcher2.ui.launcher.search
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.asLiveData
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import de.mm20.launcher2.customattrs.CustomAttributesRepository
|
|
||||||
import de.mm20.launcher2.favorites.FavoritesRepository
|
import de.mm20.launcher2.favorites.FavoritesRepository
|
||||||
import de.mm20.launcher2.permissions.PermissionGroup
|
import de.mm20.launcher2.permissions.PermissionGroup
|
||||||
import de.mm20.launcher2.permissions.PermissionsManager
|
import de.mm20.launcher2.permissions.PermissionsManager
|
||||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.SearchService
|
import de.mm20.launcher2.search.SearchService
|
||||||
import de.mm20.launcher2.search.WebsearchRepository
|
import de.mm20.launcher2.search.WebsearchRepository
|
||||||
import de.mm20.launcher2.search.data.*
|
import de.mm20.launcher2.search.data.*
|
||||||
@ -47,7 +45,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
|||||||
val unitConverterResults = MutableLiveData<List<UnitConverter>>(emptyList())
|
val unitConverterResults = MutableLiveData<List<UnitConverter>>(emptyList())
|
||||||
val websearchResults = MutableLiveData<List<Websearch>>(emptyList())
|
val websearchResults = MutableLiveData<List<Websearch>>(emptyList())
|
||||||
|
|
||||||
val hiddenResults = MutableLiveData<List<PinnableSearchable>>(emptyList())
|
val hiddenResults = MutableLiveData<List<SavableSearchable>>(emptyList())
|
||||||
|
|
||||||
val favoritesEnabled = dataStore.data.map { it.favorites.enabled }
|
val favoritesEnabled = dataStore.data.map { it.favorites.enabled }
|
||||||
val hideFavorites = MutableLiveData(false)
|
val hideFavorites = MutableLiveData(false)
|
||||||
@ -90,7 +88,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
|||||||
wikipedia = it.wikipediaSearch,
|
wikipedia = it.wikipediaSearch,
|
||||||
).collectLatest { results ->
|
).collectLatest { results ->
|
||||||
hiddenItemKeys.collectLatest { hiddenKeys ->
|
hiddenItemKeys.collectLatest { hiddenKeys ->
|
||||||
val hidden = mutableListOf<PinnableSearchable>()
|
val hidden = mutableListOf<SavableSearchable>()
|
||||||
val apps = mutableListOf<LauncherApp>()
|
val apps = mutableListOf<LauncherApp>()
|
||||||
val workApps = mutableListOf<LauncherApp>()
|
val workApps = mutableListOf<LauncherApp>()
|
||||||
val shortcuts = mutableListOf<AppShortcut>()
|
val shortcuts = mutableListOf<AppShortcut>()
|
||||||
@ -103,7 +101,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
|||||||
val website = mutableListOf<Website>()
|
val website = mutableListOf<Website>()
|
||||||
for (r in results) {
|
for (r in results) {
|
||||||
when {
|
when {
|
||||||
r is PinnableSearchable && hiddenKeys.contains(r.key) -> {
|
r is SavableSearchable && hiddenKeys.contains(r.key) -> {
|
||||||
hidden.add(r)
|
hidden.add(r)
|
||||||
}
|
}
|
||||||
r is LauncherApp && !r.isMainProfile -> workApps.add(r)
|
r is LauncherApp && !r.isMainProfile -> workApps.add(r)
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import de.mm20.launcher2.favorites.FavoritesRepository
|
|||||||
import de.mm20.launcher2.icons.IconRepository
|
import de.mm20.launcher2.icons.IconRepository
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.data.AppShortcut
|
import de.mm20.launcher2.search.data.AppShortcut
|
||||||
import de.mm20.launcher2.search.data.LauncherApp
|
import de.mm20.launcher2.search.data.LauncherApp
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@ -18,7 +18,7 @@ import org.koin.core.component.KoinComponent
|
|||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
abstract class SearchableItemVM(
|
abstract class SearchableItemVM(
|
||||||
private val searchable: PinnableSearchable
|
private val searchable: SavableSearchable
|
||||||
) : KoinComponent {
|
) : KoinComponent {
|
||||||
protected val favoritesRepository: FavoritesRepository by inject()
|
protected val favoritesRepository: FavoritesRepository by inject()
|
||||||
protected val badgeRepository: BadgeRepository by inject()
|
protected val badgeRepository: BadgeRepository by inject()
|
||||||
|
|||||||
@ -23,8 +23,7 @@ import androidx.compose.ui.unit.Dp
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import de.mm20.launcher2.badges.Badge
|
import de.mm20.launcher2.badges.Badge
|
||||||
import de.mm20.launcher2.icons.CustomIconWithPreview
|
import de.mm20.launcher2.icons.CustomIconWithPreview
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||||
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
|
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
|
||||||
@ -36,7 +35,7 @@ import kotlinx.coroutines.launch
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CustomizeSearchableSheet(
|
fun CustomizeSearchableSheet(
|
||||||
searchable: PinnableSearchable,
|
searchable: SavableSearchable,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val viewModel: CustomizeSearchableSheetVM =
|
val viewModel: CustomizeSearchableSheetVM =
|
||||||
|
|||||||
@ -7,8 +7,7 @@ import de.mm20.launcher2.customattrs.CustomIcon
|
|||||||
import de.mm20.launcher2.icons.CustomIconWithPreview
|
import de.mm20.launcher2.icons.CustomIconWithPreview
|
||||||
import de.mm20.launcher2.icons.IconRepository
|
import de.mm20.launcher2.icons.IconRepository
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
@ -16,7 +15,7 @@ import org.koin.core.component.inject
|
|||||||
import kotlin.coroutines.coroutineContext
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
class CustomizeSearchableSheetVM(
|
class CustomizeSearchableSheetVM(
|
||||||
private val searchable: PinnableSearchable
|
private val searchable: SavableSearchable
|
||||||
) : KoinComponent {
|
) : KoinComponent {
|
||||||
private val iconRepository: IconRepository by inject()
|
private val iconRepository: IconRepository by inject()
|
||||||
private val customAttributesRepository: CustomAttributesRepository by inject()
|
private val customAttributesRepository: CustomAttributesRepository by inject()
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import androidx.compose.ui.unit.IntOffset
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Popup
|
import androidx.compose.ui.window.Popup
|
||||||
import androidx.compose.ui.window.PopupProperties
|
import androidx.compose.ui.window.PopupProperties
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
import de.mm20.launcher2.search.Searchable
|
||||||
import de.mm20.launcher2.search.data.*
|
import de.mm20.launcher2.search.data.*
|
||||||
import de.mm20.launcher2.ui.component.LauncherCard
|
import de.mm20.launcher2.ui.component.LauncherCard
|
||||||
@ -45,7 +45,7 @@ import kotlinx.coroutines.delay
|
|||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GridItem(modifier: Modifier = Modifier, item: PinnableSearchable, showLabels: Boolean = true) {
|
fun GridItem(modifier: Modifier = Modifier, item: SavableSearchable, showLabels: Boolean = true) {
|
||||||
val viewModel = remember(item.key) { GridItemVM(item) }
|
val viewModel = remember(item.key) { GridItemVM(item) }
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
package de.mm20.launcher2.ui.launcher.search.common.grid
|
package de.mm20.launcher2.ui.launcher.search.common.grid
|
||||||
|
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.ui.launcher.search.common.SearchableItemVM
|
import de.mm20.launcher2.ui.launcher.search.common.SearchableItemVM
|
||||||
|
|
||||||
class GridItemVM(
|
class GridItemVM(
|
||||||
searchable: PinnableSearchable
|
searchable: SavableSearchable
|
||||||
): SearchableItemVM(searchable)
|
): SearchableItemVM(searchable)
|
||||||
@ -5,15 +5,14 @@ import androidx.compose.foundation.layout.*
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.ui.layout.BottomReversed
|
import de.mm20.launcher2.ui.layout.BottomReversed
|
||||||
import de.mm20.launcher2.ui.locals.LocalGridColumns
|
import de.mm20.launcher2.ui.locals.LocalGridColumns
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SearchResultGrid(
|
fun SearchResultGrid(
|
||||||
items: List<PinnableSearchable>,
|
items: List<SavableSearchable>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
showLabels: Boolean = true,
|
showLabels: Boolean = true,
|
||||||
columns: Int = LocalGridColumns.current,
|
columns: Int = LocalGridColumns.current,
|
||||||
|
|||||||
@ -8,8 +8,7 @@ import androidx.compose.ui.geometry.Rect
|
|||||||
import androidx.compose.ui.layout.boundsInWindow
|
import androidx.compose.ui.layout.boundsInWindow
|
||||||
import androidx.compose.ui.layout.onGloballyPositioned
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.search.data.*
|
import de.mm20.launcher2.search.data.*
|
||||||
import de.mm20.launcher2.ui.component.InnerCard
|
import de.mm20.launcher2.ui.component.InnerCard
|
||||||
import de.mm20.launcher2.ui.launcher.search.calendar.CalendarItem
|
import de.mm20.launcher2.ui.launcher.search.calendar.CalendarItem
|
||||||
@ -18,7 +17,7 @@ import de.mm20.launcher2.ui.launcher.search.files.FileItem
|
|||||||
import de.mm20.launcher2.ui.launcher.search.shortcut.AppShortcutItem
|
import de.mm20.launcher2.ui.launcher.search.shortcut.AppShortcutItem
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ListItem(modifier: Modifier = Modifier, item: PinnableSearchable) {
|
fun ListItem(modifier: Modifier = Modifier, item: SavableSearchable) {
|
||||||
var showDetails by remember { mutableStateOf(false) }
|
var showDetails by remember { mutableStateOf(false) }
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
package de.mm20.launcher2.ui.launcher.search.common.list
|
package de.mm20.launcher2.ui.launcher.search.common.list
|
||||||
|
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.ui.launcher.search.common.SearchableItemVM
|
import de.mm20.launcher2.ui.launcher.search.common.SearchableItemVM
|
||||||
|
|
||||||
class ListItemVM(
|
class ListItemVM(
|
||||||
searchable: PinnableSearchable
|
searchable: SavableSearchable
|
||||||
): SearchableItemVM(searchable)
|
): SearchableItemVM(searchable)
|
||||||
@ -8,13 +8,12 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.key
|
import androidx.compose.runtime.key
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.ui.layout.BottomReversed
|
import de.mm20.launcher2.ui.layout.BottomReversed
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SearchResultList(
|
fun SearchResultList(
|
||||||
items: List<PinnableSearchable>,
|
items: List<SavableSearchable>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
reverse: Boolean = false
|
reverse: Boolean = false
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -14,9 +14,8 @@ import de.mm20.launcher2.favorites.FavoritesRepository
|
|||||||
import de.mm20.launcher2.icons.IconRepository
|
import de.mm20.launcher2.icons.IconRepository
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.data.LauncherApp
|
import de.mm20.launcher2.search.data.LauncherApp
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
@ -33,18 +32,18 @@ class HiddenItemsSettingsScreenVM : ViewModel(), KoinComponent {
|
|||||||
val allApps = appRepository.getAllInstalledApps().map {
|
val allApps = appRepository.getAllInstalledApps().map {
|
||||||
withContext(Dispatchers.Default) { it.sorted() }
|
withContext(Dispatchers.Default) { it.sorted() }
|
||||||
}.asLiveData()
|
}.asLiveData()
|
||||||
val hiddenItems: LiveData<List<PinnableSearchable>> = liveData {
|
val hiddenItems: LiveData<List<SavableSearchable>> = liveData {
|
||||||
val hidden = withContext(Dispatchers.Default) {
|
val hidden = withContext(Dispatchers.Default) {
|
||||||
favoritesRepository.getHiddenItems().first().filter { it !is LauncherApp }.sorted()
|
favoritesRepository.getHiddenItems().first().filter { it !is LauncherApp }.sorted()
|
||||||
}
|
}
|
||||||
emit(hidden)
|
emit(hidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isHidden(searchable: PinnableSearchable): Flow<Boolean> {
|
fun isHidden(searchable: SavableSearchable): Flow<Boolean> {
|
||||||
return favoritesRepository.isHidden(searchable)
|
return favoritesRepository.isHidden(searchable)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setHidden(searchable: PinnableSearchable, hidden: Boolean) {
|
fun setHidden(searchable: SavableSearchable, hidden: Boolean) {
|
||||||
if(hidden) {
|
if(hidden) {
|
||||||
favoritesRepository.hideItem(searchable)
|
favoritesRepository.hideItem(searchable)
|
||||||
} else {
|
} else {
|
||||||
@ -52,11 +51,11 @@ class HiddenItemsSettingsScreenVM : ViewModel(), KoinComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIcon(searchable: PinnableSearchable, size: Int): Flow<LauncherIcon> {
|
fun getIcon(searchable: SavableSearchable, size: Int): Flow<LauncherIcon> {
|
||||||
return iconRepository.getIcon(searchable, size)
|
return iconRepository.getIcon(searchable, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun launch(context: Context, searchable: PinnableSearchable) {
|
fun launch(context: Context, searchable: SavableSearchable) {
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
if (isAtLeastApiLevel(31)) {
|
if (isAtLeastApiLevel(31)) {
|
||||||
bundle.putInt("android.activity.splashScreenStyle", 1)
|
bundle.putInt("android.activity.splashScreenStyle", 1)
|
||||||
|
|||||||
@ -9,8 +9,7 @@ import coil.imageLoader
|
|||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import de.mm20.launcher2.icons.*
|
import de.mm20.launcher2.icons.*
|
||||||
import de.mm20.launcher2.ktx.tryStartActivity
|
import de.mm20.launcher2.ktx.tryStartActivity
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.websites.R
|
import de.mm20.launcher2.websites.R
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
|
|
||||||
@ -22,7 +21,7 @@ data class Website(
|
|||||||
val favicon: String,
|
val favicon: String,
|
||||||
val color: Int,
|
val color: Int,
|
||||||
override val labelOverride: String? = null,
|
override val labelOverride: String? = null,
|
||||||
) : PinnableSearchable {
|
) : SavableSearchable {
|
||||||
|
|
||||||
override val domain: String = Domain
|
override val domain: String = Domain
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
package de.mm20.launcher2.websites
|
package de.mm20.launcher2.websites
|
||||||
|
|
||||||
import de.mm20.launcher2.ktx.jsonObjectOf
|
import de.mm20.launcher2.ktx.jsonObjectOf
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.SearchableDeserializer
|
import de.mm20.launcher2.search.SearchableDeserializer
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.search.data.Website
|
import de.mm20.launcher2.search.data.Website
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
class WebsiteSerializer : SearchableSerializer {
|
class WebsiteSerializer : SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String {
|
override fun serialize(searchable: SavableSearchable): String {
|
||||||
searchable as Website
|
searchable as Website
|
||||||
return jsonObjectOf(
|
return jsonObjectOf(
|
||||||
"label" to searchable.label,
|
"label" to searchable.label,
|
||||||
@ -26,7 +25,7 @@ class WebsiteSerializer : SearchableSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class WebsiteDeserializer: SearchableDeserializer {
|
class WebsiteDeserializer: SearchableDeserializer {
|
||||||
override fun deserialize(serialized: String): PinnableSearchable? {
|
override fun deserialize(serialized: String): SavableSearchable? {
|
||||||
val json = JSONObject(serialized)
|
val json = JSONObject(serialized)
|
||||||
return Website(
|
return Website(
|
||||||
label = json.getString("label"),
|
label = json.getString("label"),
|
||||||
|
|||||||
@ -11,8 +11,7 @@ import de.mm20.launcher2.icons.ColorLayer
|
|||||||
import de.mm20.launcher2.icons.StaticLauncherIcon
|
import de.mm20.launcher2.icons.StaticLauncherIcon
|
||||||
import de.mm20.launcher2.icons.TintedIconLayer
|
import de.mm20.launcher2.icons.TintedIconLayer
|
||||||
import de.mm20.launcher2.ktx.tryStartActivity
|
import de.mm20.launcher2.ktx.tryStartActivity
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.wikipedia.R
|
import de.mm20.launcher2.wikipedia.R
|
||||||
|
|
||||||
data class Wikipedia(
|
data class Wikipedia(
|
||||||
@ -22,7 +21,7 @@ data class Wikipedia(
|
|||||||
val image: String?,
|
val image: String?,
|
||||||
val wikipediaUrl: String,
|
val wikipediaUrl: String,
|
||||||
override val labelOverride: String? = null,
|
override val labelOverride: String? = null,
|
||||||
) : PinnableSearchable {
|
) : SavableSearchable {
|
||||||
|
|
||||||
override val domain: String = Domain
|
override val domain: String = Domain
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
package de.mm20.launcher2.wikipedia
|
package de.mm20.launcher2.wikipedia
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import de.mm20.launcher2.search.PinnableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.SearchableDeserializer
|
import de.mm20.launcher2.search.SearchableDeserializer
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
import de.mm20.launcher2.search.Searchable
|
|
||||||
import de.mm20.launcher2.search.data.Wikipedia
|
import de.mm20.launcher2.search.data.Wikipedia
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
class WikipediaSerializer : SearchableSerializer {
|
class WikipediaSerializer : SearchableSerializer {
|
||||||
override fun serialize(searchable: PinnableSearchable): String {
|
override fun serialize(searchable: SavableSearchable): String {
|
||||||
searchable as Wikipedia
|
searchable as Wikipedia
|
||||||
val json = JSONObject()
|
val json = JSONObject()
|
||||||
json.put("label", searchable.label)
|
json.put("label", searchable.label)
|
||||||
@ -25,7 +24,7 @@ class WikipediaSerializer : SearchableSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class WikipediaDeserializer(val context: Context) : SearchableDeserializer {
|
class WikipediaDeserializer(val context: Context) : SearchableDeserializer {
|
||||||
override fun deserialize(serialized: String): PinnableSearchable? {
|
override fun deserialize(serialized: String): SavableSearchable? {
|
||||||
val json = JSONObject(serialized)
|
val json = JSONObject(serialized)
|
||||||
return Wikipedia(
|
return Wikipedia(
|
||||||
label = json.getString("label"),
|
label = json.getString("label"),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user