FreshRSSDabatabase.kt 4.63 KB
Newer Older
Christophe Henry's avatar
Christophe Henry committed
1
package fr.chenry.android.freshrss.store.database
2

3
import android.graphics.Bitmap
4
import androidx.room.*
5
import dev.matrix.roomigrant.GenerateRoomMigrations
6 7
import fr.chenry.android.freshrss.FreshRSSApplication
import fr.chenry.android.freshrss.R
Christophe Henry's avatar
Christophe Henry committed
8 9
import fr.chenry.android.freshrss.store.api.models.StreamId
import fr.chenry.android.freshrss.store.database.models.*
10
import fr.chenry.android.freshrss.utils.Try
11
import fr.chenry.android.freshrss.utils.getOrDefault
12 13
import nl.komponents.kovenant.*
import nl.komponents.kovenant.functional.bind
14 15
import kotlin.reflect.KProperty

16
@Database(version = 3, entities = [Account::class, Article::class, Subscription::class])
Christophe Henry's avatar
Christophe Henry committed
17
@TypeConverters(Converters::class)
18
@GenerateRoomMigrations
19 20 21 22 23 24
abstract class FreshRSSDabatabase: RoomDatabase() {
    private val authTokensDelegate = AuthTokensDelegate()
    var account: Account by authTokensDelegate
    val authTokensExistInDB get() = authTokensDelegate.isInitialized

    protected abstract fun getAuthTokensDAO(): AuthTokensDAO
Christophe Henry's avatar
Christophe Henry committed
25
    protected abstract fun getArticlesDAO(): ArticlesDAO
26
    protected abstract fun getSubscriptionsDAO(): SubscriptionsDAO
Christophe Henry's avatar
Christophe Henry committed
27 28 29 30 31

    fun getArticleByStreamId(streamId: StreamId) = getArticlesDAO().getByStreamId(streamId)
    fun getArticleById(id: ItemId) = getArticlesDAO().getById(id)
    fun getArticleByStreamIdAndUnread(streamId: StreamId) =
        getArticlesDAO().getByStreamIdAndUnread(streamId, ReadStatus.UNREAD.name)
32

Christophe Henry's avatar
Christophe Henry committed
33
    fun upsertArticle(article: Article) =
34
        Try {getArticlesDAO().update(article)}.orElseDo {getArticlesDAO().insert(article)}
35

Christophe Henry's avatar
Christophe Henry committed
36
    fun insertArticle(article: Article) = getArticlesDAO().forceInsert(article)
37 38 39 40 41 42 43 44 45

    fun syncSubscriptions(subscriptions: Subscriptions) = task {
        val inDatabase = getSubscriptionsDAO().getAll().blockingFirst().map {it.id to it}.toMap()
        val inQuery = subscriptions.map {it.id to it}.toMap()
        inDatabase to inQuery
    }.bind {(inDatabase, inQuery) ->
        val suppressTask = task {
            val suppressKeys = inDatabase.entries.filter {inQuery[it.key] == null}.map {it.key}
            getSubscriptionsDAO().deleteAllById(suppressKeys)
46
        }
47 48 49 50 51 52 53
        val updateTask = task {
            inQuery.values
                .filter {s1 -> !s1.areSimilar(inDatabase[s1.id])}
                .forEach {value ->
                    getSubscriptionsDAO().updateValues(value.id, value.title, value.iconUrl)
                    if(inDatabase[value.id]?.iconUrl != value.iconUrl) getSubscriptionsDAO().deleteImage(value.id)
                }
54
        }
55 56 57 58 59 60 61 62
        val insertTask = task {
            val toInsert = inQuery.filter {inDatabase[it.key] == null}.map {it.value}
            getSubscriptionsDAO().insertAll(toInsert)
        }

        all(listOf(suppressTask, updateTask, insertTask), cancelOthersOnError = false)
    }.toSuccessVoid()

63 64 65
    fun updateSubscriptionCount(id: String, count: Int) = getSubscriptionsDAO().updateCount(id, count)
    fun incrementSubscriptionCount(id: String) = getSubscriptionsDAO().incrementCount(id)
    fun decrementSubscriptionCount(id: String) = getSubscriptionsDAO().decrementCount(id)
66
    fun insertSubscriptionImage(id: String, bitmap: Bitmap) = getSubscriptionsDAO().insertImage(id, bitmap)
67 68
    fun getAllSubcriptions() = getSubscriptionsDAO().getAll()
    fun getAllSubcriptionsIds() = getSubscriptionsDAO().getAllIds()
69
    fun getAllSubcriptionsWithImageToUpdate() = getSubscriptionsDAO().withImageToUpdate()
70 71 72 73 74 75 76

    companion object {
        private val dbName by lazy {
            "${FreshRSSApplication.context.getString(R.string.app_name).toLowerCase()}.db"
        }
        val instance by lazy {
            val instance =
77 78
                Room
                    .databaseBuilder(FreshRSSApplication.context, FreshRSSDabatabase::class.java, dbName)
79
                    .addMigrations(*FreshRSSDabatabase_Migrations.build())
Christophe Henry's avatar
Christophe Henry committed
80
                    .build()
81 82 83 84 85 86 87 88 89 90 91 92 93 94
            instance.authTokensDelegate.fetchAuthtokensFromDB()
            instance
        }
    }

    inner class AuthTokensDelegate {
        private lateinit var cachedAccount: Account
        val isInitialized
            get() = ::cachedAccount.isInitialized

        operator fun getValue(thisRef: Any?, property: KProperty<*>): Account = cachedAccount

        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Account) {
            cachedAccount = value
95
            task {getAuthTokensDAO().insert(account)}.getOrDefault(Unit)
96 97 98 99 100 101 102 103 104
        }

        fun fetchAuthtokensFromDB() {
            if(!isInitialized) {
                val authTokensFromDB = task {getAuthTokensDAO().getAuthTokens()[0]}.getOrDefault(null)
                if(authTokensFromDB != null) cachedAccount = authTokensFromDB
            }
        }
    }
Christophe Henry's avatar
Christophe Henry committed
105
}