Add favorites part to clock widget

This commit is contained in:
MM20
2022-03-31 23:02:59 +02:00
parent 93bd62545d
commit 5075c368a2
18 changed files with 298 additions and 132 deletions
@@ -17,8 +17,8 @@ data class FavoritesItem(
var launchCount: Int,
var pinPosition: Int,
var hidden: Boolean
) : KoinComponent {
private val serializer: SearchableSerializer by inject { parametersOf(searchable) }
) {
private val serializer: SearchableSerializer = getSerializer(searchable)
fun toDatabaseEntity(): FavoritesItemEntity? {
val serializer = serializer
@@ -1,21 +1,41 @@
package de.mm20.launcher2.favorites
import android.content.Context
import de.mm20.launcher2.appshortcuts.AppShortcutDeserializer
import de.mm20.launcher2.appshortcuts.AppShortcutSerializer
import de.mm20.launcher2.calendar.CalendarEventDeserializer
import de.mm20.launcher2.calendar.CalendarEventSerializer
import de.mm20.launcher2.contacts.ContactDeserializer
import de.mm20.launcher2.contacts.ContactSerializer
import de.mm20.launcher2.database.AppDatabase
import de.mm20.launcher2.database.entities.FavoritesItemEntity
import de.mm20.launcher2.files.*
import de.mm20.launcher2.ktx.ceilToInt
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.search.NullDeserializer
import de.mm20.launcher2.search.NullSerializer
import de.mm20.launcher2.search.SearchableDeserializer
import de.mm20.launcher2.search.data.CalendarEvent
import de.mm20.launcher2.search.data.Searchable
import de.mm20.launcher2.search.SearchableSerializer
import de.mm20.launcher2.search.data.*
import de.mm20.launcher2.websites.WebsiteDeserializer
import de.mm20.launcher2.websites.WebsiteSerializer
import de.mm20.launcher2.wikipedia.WikipediaDeserializer
import de.mm20.launcher2.wikipedia.WikipediaSerializer
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
import org.koin.core.parameter.parametersOf
interface FavoritesRepository {
fun getFavorites(excludeCalendarEvents: Boolean = false): Flow<List<Searchable>>
fun getFavorites(
columns: Int,
maxRows: Int? = null,
excludeCalendarEvents: Boolean = false
): Flow<List<Searchable>>
fun getPinnedCalendarEvents(): Flow<List<Searchable>>
fun isPinned(searchable: Searchable): Flow<Boolean>
fun pinItem(searchable: Searchable)
@@ -32,42 +52,42 @@ 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)
override fun getFavorites(excludeCalendarEvents: Boolean): Flow<List<Searchable>> =
override fun getFavorites(
columns: Int,
maxRows: Int?,
excludeCalendarEvents: Boolean
): Flow<List<Searchable>> =
channelFlow {
withContext(Dispatchers.IO) {
val gridColumns = dataStore.data.map { it.grid.columnCount }.distinctUntilChanged()
val dao = database.searchDao()
val pinnedFavorites = dao.getFavorites(excludeCalendarEvents).map {
it.mapNotNull {
val item = fromDatabaseEntity(it).searchable
if (item == null) {
dao.deleteByKey(it.key)
}
return@mapNotNull item
}
}
pinnedFavorites.collectLatest { pinned ->
gridColumns.collectLatest { columns ->
var favCount = (pinned.size.toDouble() / columns).ceilToInt() * columns
if (pinned.size < columns) favCount += columns
val autoFavs = dao.getAutoFavorites(favCount - pinned.size).mapNotNull {
val pinnedFavorites =
dao.getFavorites(excludeCalendarEvents, columns * (maxRows ?: 20)).map {
it.mapNotNull {
val item = fromDatabaseEntity(it).searchable
if (item == null) {
dao.deleteByKey(it.key)
}
return@mapNotNull item
}
send(pinned + autoFavs)
}
pinnedFavorites.collectLatest { pinned ->
var favCount = (pinned.size.toDouble() / columns).ceilToInt() * columns
if (pinned.size < columns) favCount += columns
val autoFavs = dao.getAutoFavorites(
favCount.coerceAtMost((maxRows ?: 20) * columns) - pinned.size
).mapNotNull {
val item = fromDatabaseEntity(it).searchable
if (item == null) {
dao.deleteByKey(it.key)
}
return@mapNotNull item
}
send(pinned + autoFavs)
}
}
}
@@ -173,7 +193,7 @@ internal class FavoritesRepositoryImpl(
private fun fromDatabaseEntity(entity: FavoritesItemEntity): FavoritesItem {
val deserializer: SearchableDeserializer = get { parametersOf(entity.serializedSearchable) }
val deserializer: SearchableDeserializer = getDeserializer(context, entity.serializedSearchable)
return FavoritesItem(
key = entity.key,
searchable = deserializer.deserialize(entity.serializedSearchable.substringAfter("#")),
@@ -18,80 +18,5 @@ import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
val favoritesModule = module {
factory { (searchable: Searchable) ->
if (searchable is LauncherApp) {
return@factory LauncherAppSerializer()
}
if (searchable is AppShortcut) {
return@factory AppShortcutSerializer()
}
if (searchable is CalendarEvent) {
return@factory CalendarEventSerializer()
}
if (searchable is Contact) {
return@factory ContactSerializer()
}
if (searchable is Wikipedia) {
return@factory WikipediaSerializer()
}
if (searchable is GDriveFile) {
return@factory GDriveFileSerializer()
}
if (searchable is OneDriveFile) {
return@factory OneDriveFileSerializer()
}
if (searchable is OwncloudFile) {
return@factory OwncloudFileSerializer()
}
if (searchable is NextcloudFile) {
return@factory NextcloudFileSerializer()
}
if (searchable is LocalFile) {
return@factory LocalFileSerializer()
}
if (searchable is Website) {
return@factory WebsiteSerializer()
}
return@factory NullSerializer()
}
factory { (serialized: String) ->
val type = serialized.substringBefore("#")
if (type == "app") {
return@factory LauncherAppDeserializer(androidContext())
}
if (type == "shortcut") {
return@factory AppShortcutDeserializer(androidContext())
}
if (type == "calendar") {
return@factory CalendarEventDeserializer(androidContext())
}
if (type == "contact") {
return@factory ContactDeserializer(androidContext())
}
if (type == "wikipedia") {
return@factory WikipediaDeserializer(androidContext())
}
if (type == "gdrive") {
return@factory GDriveFileDeserializer()
}
if (type == "onedrive") {
return@factory OneDriveFileDeserializer()
}
if (type == "nextcloud") {
return@factory NextcloudFileDeserializer()
}
if (type == "owncloud") {
return@factory OwncloudFileDeserializer()
}
if (type == "file") {
return@factory LocalFileDeserializer(androidContext())
}
if (type == "website") {
return@factory WebsiteDeserializer()
}
return@factory NullDeserializer()
}
single<FavoritesRepository> { FavoritesRepositoryImpl(androidContext(), get(), get()) }
single<FavoritesRepository> { FavoritesRepositoryImpl(androidContext(), get()) }
}
@@ -0,0 +1,95 @@
package de.mm20.launcher2.favorites
import android.content.Context
import de.mm20.launcher2.appshortcuts.AppShortcutDeserializer
import de.mm20.launcher2.appshortcuts.AppShortcutSerializer
import de.mm20.launcher2.calendar.CalendarEventDeserializer
import de.mm20.launcher2.calendar.CalendarEventSerializer
import de.mm20.launcher2.contacts.ContactDeserializer
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.SearchableDeserializer
import de.mm20.launcher2.search.SearchableSerializer
import de.mm20.launcher2.search.data.*
import de.mm20.launcher2.websites.WebsiteDeserializer
import de.mm20.launcher2.websites.WebsiteSerializer
import de.mm20.launcher2.wikipedia.WikipediaDeserializer
import de.mm20.launcher2.wikipedia.WikipediaSerializer
internal fun getSerializer(searchable: Searchable?): SearchableSerializer {
if (searchable is LauncherApp) {
return LauncherAppSerializer()
}
if (searchable is AppShortcut) {
return AppShortcutSerializer()
}
if (searchable is CalendarEvent) {
return CalendarEventSerializer()
}
if (searchable is Contact) {
return ContactSerializer()
}
if (searchable is Wikipedia) {
return WikipediaSerializer()
}
if (searchable is GDriveFile) {
return GDriveFileSerializer()
}
if (searchable is OneDriveFile) {
return OneDriveFileSerializer()
}
if (searchable is OwncloudFile) {
return OwncloudFileSerializer()
}
if (searchable is NextcloudFile) {
return NextcloudFileSerializer()
}
if (searchable is LocalFile) {
return LocalFileSerializer()
}
if (searchable is Website) {
return WebsiteSerializer()
}
return NullSerializer()
}
internal fun getDeserializer(context: Context, serialized: String): SearchableDeserializer {
val type = serialized.substringBefore("#")
if (type == "app") {
return LauncherAppDeserializer(context)
}
if (type == "shortcut") {
return AppShortcutDeserializer(context)
}
if (type == "calendar") {
return CalendarEventDeserializer(context)
}
if (type == "contact") {
return ContactDeserializer(context)
}
if (type == "wikipedia") {
return WikipediaDeserializer(context)
}
if (type == "gdrive") {
return GDriveFileDeserializer()
}
if (type == "onedrive") {
return OneDriveFileDeserializer()
}
if (type == "nextcloud") {
return NextcloudFileDeserializer()
}
if (type == "owncloud") {
return OwncloudFileDeserializer()
}
if (type == "file") {
return LocalFileDeserializer(context)
}
if (type == "website") {
return WebsiteDeserializer()
}
return NullDeserializer()
}