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