Rename
- PinnableSearchable -> SavableSearchable, - FavoritesItem -> SavedSearchable, - FavoritesItemEntity -> SavedSearchableEntity add type column to searchable table
This commit is contained in:
@@ -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"),
|
||||
|
||||
+9
-11
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user