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:
Christoph
2023-03-10 22:17:03 +01:00
committed by GitHub
co-authored by MM20
parent 003789f310
commit 2c9cba72a9
21 changed files with 838 additions and 163 deletions
@@ -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
)
}
}