Search: introduce weighted reordering (#272)
* Database: backend for weights & timestamps - Migration 21->22 - SearchableLaunchTimestampEntity.kt - `weight`-column in SavedSearchableEntity - Queries for sorting by and adjusting weight - Queries for sorting by recent use * move weightfactor access to favoritesRepository * reorder calls in SearchVM * no more datahoarding * add settings screen for ordering * ui fix, animations * move ordering settings out of own screen * remove unused localization * weight factors * larger factor for WeightFactor.High * cleanup * sort favorites by weight * icons for favorite screen * I hate coming up with descriptive strings * add default weight factor to settings migration * Add default values for search result ordering preferences * Favorites settings: change order of preferences * Change strings * Replace favorites variability slider with list preference * migration initial weight * Change labels * Include searchable weight column in backup --------- Co-authored-by: MM20 <15646950+MM2-0@users.noreply.github.com>
This commit is contained in:
@@ -6,6 +6,8 @@ import de.mm20.launcher2.crashreporter.CrashReporter
|
||||
import de.mm20.launcher2.database.AppDatabase
|
||||
import de.mm20.launcher2.database.entities.SavedSearchableEntity
|
||||
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.*
|
||||
@@ -58,6 +60,8 @@ interface FavoritesRepository {
|
||||
*/
|
||||
fun sortByRelevance(keys: List<String>): Flow<List<String>>
|
||||
|
||||
fun sortByWeight(keys: List<String>): Flow<List<String>>
|
||||
|
||||
/**
|
||||
* Remove this item from the Searchable database
|
||||
*/
|
||||
@@ -94,6 +98,7 @@ interface FavoritesRepository {
|
||||
internal class FavoritesRepositoryImpl(
|
||||
private val context: Context,
|
||||
private val database: AppDatabase,
|
||||
private val dataStore: LauncherDataStore
|
||||
) : FavoritesRepository, KoinComponent {
|
||||
|
||||
private val scope = CoroutineScope(Job() + Dispatchers.Default)
|
||||
@@ -160,7 +165,8 @@ internal class FavoritesRepositoryImpl(
|
||||
searchable = searchable,
|
||||
launchCount = databaseItem?.launchCount ?: 0,
|
||||
pinPosition = 1,
|
||||
hidden = false
|
||||
hidden = false,
|
||||
weight = databaseItem?.weight ?: 0.0
|
||||
)
|
||||
savedSearchable.toDatabaseEntity()?.let { dao.insertReplaceExisting(it) }
|
||||
}
|
||||
@@ -189,7 +195,8 @@ internal class FavoritesRepositoryImpl(
|
||||
searchable = searchable,
|
||||
launchCount = databaseItem?.launchCount ?: 0,
|
||||
pinPosition = 0,
|
||||
hidden = true
|
||||
hidden = true,
|
||||
weight = databaseItem?.weight ?: 0.0
|
||||
)
|
||||
savedSearchable.toDatabaseEntity()?.let { dao.insertReplaceExisting(it) }
|
||||
}
|
||||
@@ -207,10 +214,16 @@ internal class FavoritesRepositoryImpl(
|
||||
override fun incrementLaunchCounter(searchable: SavableSearchable) {
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val item = SavedSearchable(searchable.key, searchable, 0, 0, false)
|
||||
val weightFactor =
|
||||
when (dataStore.data.map { it.resultOrdering.weightFactor }.firstOrNull()) {
|
||||
WeightFactor.Low -> 0.1
|
||||
WeightFactor.High -> 0.5
|
||||
else -> 0.2
|
||||
}
|
||||
val item = SavedSearchable(searchable.key, searchable, 0, 0, false, 0.0)
|
||||
item.toDatabaseEntity()?.let {
|
||||
database.searchDao()
|
||||
.incrementLaunchCount(it)
|
||||
.incrementLaunchCount(it, weightFactor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,6 +262,7 @@ internal class FavoritesRepositoryImpl(
|
||||
launchCount = 0,
|
||||
pinPosition = 0,
|
||||
hidden = false,
|
||||
weight = 0.0
|
||||
).toDatabaseEntity() ?: return@withContext
|
||||
database.searchDao().insertSkipExisting(entity)
|
||||
}
|
||||
@@ -271,6 +285,7 @@ internal class FavoritesRepositoryImpl(
|
||||
launchCount = 0,
|
||||
pinPosition = 0,
|
||||
hidden = false,
|
||||
weight = 0.0
|
||||
).toDatabaseEntity() ?: return@mapIndexedNotNull null
|
||||
entity.pinPosition = manuallySorted.size - index + 1
|
||||
entity
|
||||
@@ -283,6 +298,7 @@ internal class FavoritesRepositoryImpl(
|
||||
launchCount = 0,
|
||||
pinPosition = 0,
|
||||
hidden = false,
|
||||
weight = 0.0
|
||||
).toDatabaseEntity() ?: return@mapIndexedNotNull null
|
||||
entity.pinPosition = 1
|
||||
entity
|
||||
@@ -300,6 +316,10 @@ internal class FavoritesRepositoryImpl(
|
||||
return database.searchDao().sortByRelevance(keys)
|
||||
}
|
||||
|
||||
override fun sortByWeight(keys: List<String>): Flow<List<String>> {
|
||||
return database.searchDao().sortByWeight(keys)
|
||||
}
|
||||
|
||||
private fun fromDatabaseEntity(entity: SavedSearchableEntity): SavedSearchable {
|
||||
val deserializer: SearchableDeserializer =
|
||||
getDeserializer(context, entity.type)
|
||||
@@ -310,7 +330,8 @@ internal class FavoritesRepositoryImpl(
|
||||
searchable = searchable,
|
||||
launchCount = entity.launchCount,
|
||||
pinPosition = entity.pinPosition,
|
||||
hidden = entity.hidden
|
||||
hidden = entity.hidden,
|
||||
weight = entity.weight
|
||||
)
|
||||
}
|
||||
|
||||
@@ -340,7 +361,8 @@ internal class FavoritesRepositoryImpl(
|
||||
"hidden" to fav.hidden,
|
||||
"launchCount" to fav.launchCount,
|
||||
"pinPosition" to fav.pinPosition,
|
||||
"searchable" to fav.serializedSearchable
|
||||
"searchable" to fav.serializedSearchable,
|
||||
"weight" to fav.weight,
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -373,7 +395,8 @@ internal class FavoritesRepositoryImpl(
|
||||
serializedSearchable = json.getString("searchable"),
|
||||
launchCount = json.getInt("launchCount"),
|
||||
hidden = json.getBoolean("hidden"),
|
||||
pinPosition = json.getInt("pinPosition")
|
||||
pinPosition = json.getInt("pinPosition"),
|
||||
weight = json.optDouble("weight").takeIf { !it.isNaN() } ?: 0.0
|
||||
)
|
||||
favorites.add(entity)
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@ import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.dsl.module
|
||||
|
||||
val favoritesModule = module {
|
||||
single<FavoritesRepository> { FavoritesRepositoryImpl(androidContext(), get()) }
|
||||
single<FavoritesRepository> { FavoritesRepositoryImpl(androidContext(), get(), get()) }
|
||||
}
|
||||
@@ -11,7 +11,8 @@ data class SavedSearchable(
|
||||
val searchable: SavableSearchable?,
|
||||
var launchCount: Int,
|
||||
var pinPosition: Int,
|
||||
var hidden: Boolean
|
||||
var hidden: Boolean,
|
||||
var weight: Double
|
||||
) {
|
||||
fun toDatabaseEntity(): SavedSearchableEntity? {
|
||||
val serializer = getSerializer(searchable)
|
||||
@@ -24,7 +25,8 @@ data class SavedSearchable(
|
||||
serializedSearchable = data,
|
||||
hidden = hidden,
|
||||
pinPosition = pinPosition,
|
||||
launchCount = launchCount
|
||||
launchCount = launchCount,
|
||||
weight = weight
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user