Refactor favorites and searchable database
This commit is contained in:
@@ -44,5 +44,5 @@ dependencies {
|
||||
implementation(project(":core:base"))
|
||||
implementation(project(":core:ktx"))
|
||||
implementation(project(":core:crashreporter"))
|
||||
implementation(project(":data:favorites"))
|
||||
implementation(project(":data:searchable"))
|
||||
}
|
||||
+7
-7
@@ -3,7 +3,7 @@ package de.mm20.launcher2.data.customattrs
|
||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||
import de.mm20.launcher2.database.AppDatabase
|
||||
import de.mm20.launcher2.database.entities.CustomAttributeEntity
|
||||
import de.mm20.launcher2.favorites.FavoritesRepository
|
||||
import de.mm20.launcher2.searchable.SearchableRepository
|
||||
import de.mm20.launcher2.ktx.jsonObjectOf
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -46,7 +46,7 @@ interface CustomAttributesRepository {
|
||||
|
||||
internal class CustomAttributesRepositoryImpl(
|
||||
private val appDatabase: AppDatabase,
|
||||
private val favoritesRepository: FavoritesRepository
|
||||
private val searchableRepository: SearchableRepository
|
||||
) : CustomAttributesRepository {
|
||||
private val scope = CoroutineScope(Job() + Dispatchers.Default)
|
||||
|
||||
@@ -79,7 +79,7 @@ internal class CustomAttributesRepositoryImpl(
|
||||
override fun setCustomLabel(searchable: SavableSearchable, label: String) {
|
||||
val dao = appDatabase.customAttrsDao()
|
||||
scope.launch {
|
||||
favoritesRepository.save(searchable)
|
||||
searchableRepository.insert(searchable)
|
||||
appDatabase.runInTransaction {
|
||||
dao.clearCustomAttribute(searchable.key, CustomAttributeType.Label.value)
|
||||
dao.setCustomAttribute(
|
||||
@@ -102,7 +102,7 @@ internal class CustomAttributesRepositoryImpl(
|
||||
override fun setTags(searchable: SavableSearchable, tags: List<String>) {
|
||||
val dao = appDatabase.customAttrsDao()
|
||||
scope.launch {
|
||||
favoritesRepository.save(searchable)
|
||||
searchableRepository.insert(searchable)
|
||||
dao.setTags(searchable.key, tags.map {
|
||||
CustomTag(it).toDatabaseEntity(searchable.key)
|
||||
})
|
||||
@@ -128,7 +128,7 @@ internal class CustomAttributesRepositoryImpl(
|
||||
override fun getItemsForTag(tag: String): Flow<List<SavableSearchable>> {
|
||||
val dao = appDatabase.customAttrsDao()
|
||||
return dao.getItemsWithTag(tag).map {
|
||||
favoritesRepository.getFromKeys(it)
|
||||
searchableRepository.getByKeys(it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ internal class CustomAttributesRepositoryImpl(
|
||||
return scope.launch {
|
||||
dao.setItemsWithTag(tag, items.map { it.key })
|
||||
for (item in items) {
|
||||
favoritesRepository.save(item)
|
||||
searchableRepository.insert(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,7 +172,7 @@ internal class CustomAttributesRepositoryImpl(
|
||||
}
|
||||
val dao = appDatabase.customAttrsDao()
|
||||
return dao.search("%$query%").map {
|
||||
favoritesRepository.getFromKeys(it).toImmutableList()
|
||||
searchableRepository.getByKeys(it).toImmutableList()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package de.mm20.launcher2.favorites
|
||||
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.dsl.module
|
||||
|
||||
val favoritesModule = module {
|
||||
single<FavoritesRepository> { FavoritesRepositoryImpl(androidContext(), get(), get()) }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.mm20.launcher2.searchable
|
||||
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.dsl.module
|
||||
|
||||
val searchableModule = module {
|
||||
single<SearchableRepository> { SearchableRepositoryImpl(androidContext(), get(), get()) }
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package de.mm20.launcher2.favorites
|
||||
package de.mm20.launcher2.searchable
|
||||
|
||||
import de.mm20.launcher2.database.entities.SavedSearchableEntity
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package de.mm20.launcher2.favorites
|
||||
package de.mm20.launcher2.searchable
|
||||
|
||||
data class SavedSearchableRankInfo(
|
||||
val key: String,
|
||||
+234
-209
@@ -1,58 +1,87 @@
|
||||
package de.mm20.launcher2.favorites
|
||||
package de.mm20.launcher2.searchable
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.room.withTransaction
|
||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||
import de.mm20.launcher2.database.AppDatabase
|
||||
import de.mm20.launcher2.database.entities.SavedSearchableEntity
|
||||
import de.mm20.launcher2.database.entities.SavedSearchableUpdatePinEntity
|
||||
import de.mm20.launcher2.ktx.jsonObjectOf
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings.SearchResultOrderingSettings.WeightFactor
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.search.SearchableDeserializer
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.koin.core.component.KoinComponent
|
||||
import java.io.File
|
||||
|
||||
interface FavoritesRepository {
|
||||
interface SearchableRepository {
|
||||
|
||||
fun insert(
|
||||
searchable: SavableSearchable,
|
||||
)
|
||||
|
||||
fun upsert(
|
||||
searchable: SavableSearchable,
|
||||
hidden: Boolean? = null,
|
||||
pinned: Boolean? = null,
|
||||
launchCount: Int? = null,
|
||||
weight: Double? = null,
|
||||
)
|
||||
|
||||
fun update(
|
||||
searchable: SavableSearchable,
|
||||
hidden: Boolean? = null,
|
||||
pinned: Boolean? = null,
|
||||
launchCount: Int? = null,
|
||||
weight: Double? = null,
|
||||
)
|
||||
|
||||
/**
|
||||
* Get favorites
|
||||
* @param includeTypes Include only items of these types. Cannot be used together with excludeTypes.
|
||||
* @param excludeTypes Exclude only items of these types. Cannot be used together with includeTypes.
|
||||
* @param manuallySorted Include items that have been sorted manually
|
||||
* @param automaticallySorted Include items that are pinned but not sorted
|
||||
* @param frequentlyUsed Include items that are not pinned but most frequently used
|
||||
* @param limit Maximum number of items returned.
|
||||
*/
|
||||
fun getFavorites(
|
||||
* Touch a searchable to update its weight and launch counter
|
||||
**/
|
||||
fun touch(
|
||||
searchable: SavableSearchable,
|
||||
)
|
||||
|
||||
fun get(
|
||||
includeTypes: List<String>? = null,
|
||||
excludeTypes: List<String>? = null,
|
||||
manuallySorted: Boolean = false,
|
||||
automaticallySorted: Boolean = false,
|
||||
frequentlyUsed: Boolean = false,
|
||||
limit: Int = 100
|
||||
hidden: Boolean = false,
|
||||
limit: Int = 100,
|
||||
): Flow<List<SavableSearchable>>
|
||||
|
||||
fun getKeys(
|
||||
includeTypes: List<String>? = null,
|
||||
excludeTypes: List<String>? = null,
|
||||
manuallySorted: Boolean = false,
|
||||
automaticallySorted: Boolean = false,
|
||||
frequentlyUsed: Boolean = false,
|
||||
hidden: Boolean = false,
|
||||
limit: Int = 100,
|
||||
): Flow<List<String>>
|
||||
|
||||
|
||||
fun getHiddenCalendarEventKeys(): Flow<List<String>>
|
||||
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<SavableSearchable>,
|
||||
automaticallySorted: List<SavableSearchable>,
|
||||
)
|
||||
|
||||
fun getHiddenItems(): Flow<List<SavableSearchable>>
|
||||
fun getHiddenItemKeys(): Flow<List<String>>
|
||||
|
||||
/**
|
||||
* Returns the given keys sorted by relevance.
|
||||
* The first item in the list is the most relevant.
|
||||
@@ -65,24 +94,13 @@ interface FavoritesRepository {
|
||||
/**
|
||||
* Remove this item from the Searchable database
|
||||
*/
|
||||
fun remove(searchable: SavableSearchable)
|
||||
|
||||
/**
|
||||
* Remove this item from favorites and reset launch counter
|
||||
*/
|
||||
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: SavableSearchable)
|
||||
fun delete(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<SavableSearchable>
|
||||
suspend fun getByKeys(keys: List<String>): List<SavableSearchable>
|
||||
|
||||
suspend fun export(toDir: File)
|
||||
suspend fun import(fromDir: File)
|
||||
@@ -95,177 +113,193 @@ interface FavoritesRepository {
|
||||
suspend fun cleanupDatabase(): Int
|
||||
}
|
||||
|
||||
internal class FavoritesRepositoryImpl(
|
||||
internal class SearchableRepositoryImpl(
|
||||
private val context: Context,
|
||||
private val database: AppDatabase,
|
||||
private val dataStore: LauncherDataStore
|
||||
) : FavoritesRepository, KoinComponent {
|
||||
) : SearchableRepository, KoinComponent {
|
||||
|
||||
private val scope = CoroutineScope(Job() + Dispatchers.Default)
|
||||
|
||||
override fun getFavorites(
|
||||
override fun insert(searchable: SavableSearchable) {
|
||||
val dao = database.searchableDao()
|
||||
scope.launch {
|
||||
dao.insert(
|
||||
SavedSearchableEntity(
|
||||
key = searchable.key,
|
||||
type = searchable.domain,
|
||||
serializedSearchable = searchable.serialize() ?: return@launch,
|
||||
hidden = false,
|
||||
launchCount = 0,
|
||||
weight = 0.0,
|
||||
pinPosition = 0,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun upsert(
|
||||
searchable: SavableSearchable,
|
||||
hidden: Boolean?,
|
||||
pinned: Boolean?,
|
||||
launchCount: Int?,
|
||||
weight: Double?
|
||||
) {
|
||||
val dao = database.searchableDao()
|
||||
scope.launch {
|
||||
val entity = dao.getByKey(searchable.key).firstOrNull()
|
||||
dao.upsert(
|
||||
SavedSearchableEntity(
|
||||
key = searchable.key,
|
||||
type = searchable.domain,
|
||||
hidden = hidden ?: entity?.hidden ?: false,
|
||||
pinPosition = pinned?.let { if (it) 1 else 0 } ?: entity?.pinPosition ?: 0,
|
||||
launchCount = launchCount ?: entity?.launchCount ?: 0,
|
||||
weight = weight ?: entity?.weight ?: 0.0,
|
||||
serializedSearchable = searchable.serialize() ?: return@launch,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun update(
|
||||
searchable: SavableSearchable,
|
||||
hidden: Boolean?,
|
||||
pinned: Boolean?,
|
||||
launchCount: Int?,
|
||||
weight: Double?
|
||||
) {
|
||||
val dao = database.searchableDao()
|
||||
scope.launch {
|
||||
val entity = dao.getByKey(searchable.key).firstOrNull()
|
||||
dao.upsert(
|
||||
SavedSearchableEntity(
|
||||
key = searchable.key,
|
||||
type = searchable.domain,
|
||||
hidden = hidden ?: entity?.hidden ?: false,
|
||||
pinPosition = pinned?.let { if (it) 1 else 0 } ?: entity?.pinPosition ?: 0,
|
||||
launchCount = launchCount ?: entity?.launchCount ?: 0,
|
||||
weight = weight ?: entity?.weight ?: 0.0,
|
||||
serializedSearchable = searchable.serialize() ?: return@launch,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun touch(searchable: SavableSearchable) {
|
||||
scope.launch {
|
||||
val weightFactor =
|
||||
when (dataStore.data.map { it.resultOrdering.weightFactor }.firstOrNull()) {
|
||||
WeightFactor.Low -> WEIGHT_FACTOR_LOW
|
||||
WeightFactor.High -> WEIGHT_FACTOR_HIGH
|
||||
else -> WEIGHT_FACTOR_MEDIUM
|
||||
}
|
||||
val item = SavedSearchable(searchable.key, searchable, 0, 0, false, 0.0)
|
||||
item.toDatabaseEntity()?.let {
|
||||
database.searchableDao()
|
||||
.touch(it, weightFactor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(
|
||||
includeTypes: List<String>?,
|
||||
excludeTypes: List<String>?,
|
||||
manuallySorted: Boolean,
|
||||
automaticallySorted: Boolean,
|
||||
frequentlyUsed: Boolean,
|
||||
hidden: Boolean,
|
||||
limit: Int
|
||||
): Flow<List<SavableSearchable>> {
|
||||
val dao = database.searchDao()
|
||||
val dao = database.searchableDao()
|
||||
val entities = when {
|
||||
includeTypes == null && excludeTypes == null -> dao.getFavorites(
|
||||
includeTypes == null && excludeTypes == null -> dao.get(
|
||||
manuallySorted = manuallySorted,
|
||||
automaticallySorted = automaticallySorted,
|
||||
frequentlyUsed = frequentlyUsed,
|
||||
hidden = hidden,
|
||||
limit = limit
|
||||
)
|
||||
|
||||
includeTypes != null && excludeTypes == null -> {
|
||||
dao.getFavoritesWithTypes(
|
||||
includeTypes = includeTypes,
|
||||
manuallySorted = manuallySorted,
|
||||
automaticallySorted = automaticallySorted,
|
||||
frequentlyUsed = frequentlyUsed,
|
||||
limit = limit
|
||||
)
|
||||
}
|
||||
includeTypes == null -> dao.getExcludeTypes(
|
||||
excludeTypes = excludeTypes,
|
||||
manuallySorted = manuallySorted,
|
||||
automaticallySorted = automaticallySorted,
|
||||
frequentlyUsed = frequentlyUsed,
|
||||
hidden = hidden,
|
||||
limit = limit
|
||||
)
|
||||
|
||||
excludeTypes != null && includeTypes == null -> {
|
||||
dao.getFavoritesWithoutTypes(
|
||||
excludeTypes = excludeTypes,
|
||||
manuallySorted = manuallySorted,
|
||||
automaticallySorted = automaticallySorted,
|
||||
frequentlyUsed = frequentlyUsed,
|
||||
limit = limit
|
||||
)
|
||||
}
|
||||
excludeTypes == null -> dao.getIncludeTypes(
|
||||
includeTypes = includeTypes,
|
||||
manuallySorted = manuallySorted,
|
||||
automaticallySorted = automaticallySorted,
|
||||
frequentlyUsed = frequentlyUsed,
|
||||
hidden = hidden,
|
||||
limit = limit
|
||||
)
|
||||
|
||||
else -> throw IllegalArgumentException("You can either use includeTypes or excludeTypes, not both")
|
||||
else -> throw IllegalArgumentException("Cannot specify both includeTypes and excludeTypes")
|
||||
}
|
||||
|
||||
return entities.map {
|
||||
it.mapNotNull { fromDatabaseEntity(it).searchable }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getHiddenCalendarEventKeys(): Flow<List<String>> {
|
||||
return database.searchDao().getHiddenCalendarEventKeys()
|
||||
override fun getKeys(
|
||||
includeTypes: List<String>?,
|
||||
excludeTypes: List<String>?,
|
||||
manuallySorted: Boolean,
|
||||
automaticallySorted: Boolean,
|
||||
frequentlyUsed: Boolean,
|
||||
hidden: Boolean,
|
||||
limit: Int
|
||||
): Flow<List<String>> {
|
||||
val dao = database.searchableDao()
|
||||
return when {
|
||||
includeTypes == null && excludeTypes == null -> dao.getKeys(
|
||||
manuallySorted = manuallySorted,
|
||||
automaticallySorted = automaticallySorted,
|
||||
frequentlyUsed = frequentlyUsed,
|
||||
hidden = hidden,
|
||||
limit = limit
|
||||
)
|
||||
|
||||
includeTypes == null -> dao.getKeysExcludeTypes(
|
||||
excludeTypes = excludeTypes,
|
||||
manuallySorted = manuallySorted,
|
||||
automaticallySorted = automaticallySorted,
|
||||
frequentlyUsed = frequentlyUsed,
|
||||
hidden = hidden,
|
||||
limit = limit
|
||||
)
|
||||
|
||||
excludeTypes == null -> dao.getKeysIncludeTypes(
|
||||
includeTypes = includeTypes,
|
||||
manuallySorted = manuallySorted,
|
||||
automaticallySorted = automaticallySorted,
|
||||
frequentlyUsed = frequentlyUsed,
|
||||
hidden = hidden,
|
||||
limit = limit
|
||||
)
|
||||
|
||||
else -> throw IllegalArgumentException("Cannot specify both includeTypes and excludeTypes")
|
||||
}
|
||||
}
|
||||
|
||||
override fun isPinned(searchable: SavableSearchable): Flow<Boolean> {
|
||||
return database.searchDao().isPinned(searchable.key)
|
||||
}
|
||||
|
||||
override fun pinItem(searchable: SavableSearchable) {
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val dao = database.searchDao()
|
||||
val databaseItem = dao.getFavorite(searchable.key)
|
||||
val savedSearchable = SavedSearchable(
|
||||
key = searchable.key,
|
||||
searchable = searchable,
|
||||
launchCount = databaseItem?.launchCount ?: 0,
|
||||
pinPosition = 1,
|
||||
hidden = false,
|
||||
weight = databaseItem?.weight ?: 0.0
|
||||
)
|
||||
savedSearchable.toDatabaseEntity()?.let { dao.insertReplaceExisting(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun unpinItem(searchable: SavableSearchable) {
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
database.searchDao().unpinFavorite(searchable.key)
|
||||
}
|
||||
}
|
||||
return database.searchableDao().isPinned(searchable.key)
|
||||
}
|
||||
|
||||
override fun isHidden(searchable: SavableSearchable): Flow<Boolean> {
|
||||
return database.searchDao().isHidden(searchable.key)
|
||||
return database.searchableDao().isHidden(searchable.key)
|
||||
}
|
||||
|
||||
override fun hideItem(searchable: SavableSearchable) {
|
||||
override fun delete(searchable: SavableSearchable) {
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val dao = database.searchDao()
|
||||
val databaseItem = dao.getFavorite(searchable.key)
|
||||
val savedSearchable = SavedSearchable(
|
||||
key = searchable.key,
|
||||
searchable = searchable,
|
||||
launchCount = databaseItem?.launchCount ?: 0,
|
||||
pinPosition = 0,
|
||||
hidden = true,
|
||||
weight = databaseItem?.weight ?: 0.0
|
||||
)
|
||||
savedSearchable.toDatabaseEntity()?.let { dao.insertReplaceExisting(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun unhideItem(searchable: SavableSearchable) {
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
database.searchDao().unhideItem(searchable.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun incrementLaunchCounter(searchable: SavableSearchable) {
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val weightFactor =
|
||||
when (dataStore.data.map { it.resultOrdering.weightFactor }.firstOrNull()) {
|
||||
WeightFactor.Low -> WEIGHT_FACTOR_LOW
|
||||
WeightFactor.High -> WEIGHT_FACTOR_HIGH
|
||||
else -> WEIGHT_FACTOR_MEDIUM
|
||||
}
|
||||
val item = SavedSearchable(searchable.key, searchable, 0, 0, false, 0.0)
|
||||
item.toDatabaseEntity()?.let {
|
||||
database.searchDao()
|
||||
.incrementLaunchCount(it, weightFactor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getHiddenItems(): Flow<List<SavableSearchable>> {
|
||||
return database.searchDao().getHiddenItems().map {
|
||||
it.mapNotNull { fromDatabaseEntity(it).searchable }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getHiddenItemKeys(): Flow<List<String>> {
|
||||
return database.searchDao().getHiddenItemKeys()
|
||||
}
|
||||
|
||||
override fun remove(searchable: SavableSearchable) {
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
database.searchDao().deleteByKey(searchable.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeFromFavorites(searchable: SavableSearchable) {
|
||||
scope.launch {
|
||||
database.searchDao().resetPinStatusAndLaunchCounter(searchable.key)
|
||||
}
|
||||
}
|
||||
|
||||
override fun save(searchable: SavableSearchable) {
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val entity = SavedSearchable(
|
||||
key = searchable.key,
|
||||
searchable = searchable,
|
||||
launchCount = 0,
|
||||
pinPosition = 0,
|
||||
hidden = false,
|
||||
weight = 0.0
|
||||
).toDatabaseEntity() ?: return@withContext
|
||||
database.searchDao().insertSkipExisting(entity)
|
||||
}
|
||||
database.searchableDao().delete(searchable.key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,51 +307,42 @@ internal class FavoritesRepositoryImpl(
|
||||
manuallySorted: List<SavableSearchable>,
|
||||
automaticallySorted: List<SavableSearchable>
|
||||
) {
|
||||
val dao = database.searchDao()
|
||||
val dao = database.searchableDao()
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
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 } ?: SavedSearchable(
|
||||
key = searchable.key,
|
||||
searchable = searchable,
|
||||
launchCount = 0,
|
||||
pinPosition = 0,
|
||||
hidden = false,
|
||||
weight = 0.0
|
||||
).toDatabaseEntity() ?: return@mapIndexedNotNull null
|
||||
entity.pinPosition = manuallySorted.size - index + 1
|
||||
entity
|
||||
}
|
||||
val updatedAutomaticallySorted =
|
||||
automaticallySorted.mapIndexedNotNull { index, searchable ->
|
||||
val entity = entities.find { searchable.key == it.key } ?: SavedSearchable(
|
||||
key = searchable.key,
|
||||
searchable = searchable,
|
||||
launchCount = 0,
|
||||
pinPosition = 0,
|
||||
hidden = false,
|
||||
weight = 0.0
|
||||
).toDatabaseEntity() ?: return@mapIndexedNotNull null
|
||||
entity.pinPosition = 1
|
||||
entity
|
||||
database.withTransaction {
|
||||
dao.unpinAll()
|
||||
dao.upsert(
|
||||
manuallySorted.mapIndexedNotNull { index, savableSearchable ->
|
||||
SavedSearchableUpdatePinEntity(
|
||||
key = savableSearchable.key,
|
||||
type = savableSearchable.domain,
|
||||
pinPosition = manuallySorted.size - index + 1,
|
||||
serializedSearchable = savableSearchable.serialize()
|
||||
?: return@mapIndexedNotNull null,
|
||||
)
|
||||
}
|
||||
database.runInTransaction {
|
||||
dao.unpinAll()
|
||||
dao.insertAllReplaceExisting(updatedManuallySorted)
|
||||
dao.insertAllReplaceExisting(updatedAutomaticallySorted)
|
||||
}
|
||||
)
|
||||
dao.upsert(
|
||||
automaticallySorted.mapNotNull { savableSearchable ->
|
||||
SavedSearchableUpdatePinEntity(
|
||||
key = savableSearchable.key,
|
||||
type = savableSearchable.domain,
|
||||
pinPosition = 1,
|
||||
serializedSearchable = savableSearchable.serialize()
|
||||
?: return@mapNotNull null,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun sortByRelevance(keys: List<String>): Flow<List<String>> {
|
||||
return database.searchDao().sortByRelevance(keys)
|
||||
return database.searchableDao().sortByRelevance(keys)
|
||||
}
|
||||
|
||||
override fun sortByWeight(keys: List<String>): Flow<List<String>> {
|
||||
return database.searchDao().sortByWeight(keys)
|
||||
return database.searchableDao().sortByWeight(keys)
|
||||
}
|
||||
|
||||
private fun fromDatabaseEntity(entity: SavedSearchableEntity): SavedSearchable {
|
||||
@@ -337,13 +362,13 @@ internal class FavoritesRepositoryImpl(
|
||||
|
||||
private fun removeInvalidItem(key: String) {
|
||||
scope.launch {
|
||||
database.searchDao().deleteByKey(key)
|
||||
database.searchableDao().delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getFromKeys(keys: List<String>): List<SavableSearchable> {
|
||||
val dao = database.searchDao()
|
||||
return dao.getFromKeys(keys)
|
||||
override suspend fun getByKeys(keys: List<String>): List<SavableSearchable> {
|
||||
val dao = database.searchableDao()
|
||||
return dao.getByKeys(keys)
|
||||
.mapNotNull { fromDatabaseEntity(it).searchable }
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,4 +1,4 @@
|
||||
package de.mm20.launcher2.favorites
|
||||
package de.mm20.launcher2.searchable
|
||||
|
||||
import android.content.Context
|
||||
import de.mm20.launcher2.appshortcuts.LauncherShortcutDeserializer
|
||||
@@ -12,6 +12,7 @@ import de.mm20.launcher2.contacts.ContactSerializer
|
||||
import de.mm20.launcher2.files.*
|
||||
import de.mm20.launcher2.search.NullDeserializer
|
||||
import de.mm20.launcher2.search.NullSerializer
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.search.Searchable
|
||||
import de.mm20.launcher2.search.SearchableDeserializer
|
||||
import de.mm20.launcher2.search.SearchableSerializer
|
||||
@@ -21,6 +22,10 @@ import de.mm20.launcher2.websites.WebsiteSerializer
|
||||
import de.mm20.launcher2.wikipedia.WikipediaDeserializer
|
||||
import de.mm20.launcher2.wikipedia.WikipediaSerializer
|
||||
|
||||
internal fun SavableSearchable.serialize(): String? {
|
||||
val serializer = getSerializer(this)
|
||||
return serializer.serialize(this)
|
||||
}
|
||||
|
||||
internal fun getSerializer(searchable: Searchable?): SearchableSerializer {
|
||||
if (searchable is LauncherApp) {
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package de.mm20.launcher2.favorites
|
||||
package de.mm20.launcher2.searchable
|
||||
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.search.SearchableDeserializer
|
||||
Reference in New Issue
Block a user