FreshRSSDabatabase.kt 4.98 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
import org.joda.time.LocalDateTime
15 16
import kotlin.reflect.KProperty

17
@Database(version = 5, entities = [Account::class, Article::class, Subscription::class])
Christophe Henry's avatar
Christophe Henry committed
18
@TypeConverters(Converters::class)
19
@GenerateRoomMigrations
20 21 22 23 24 25
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
26
    protected abstract fun getArticlesDAO(): ArticlesDAO
27
    protected abstract fun getSubscriptionsDAO(): SubscriptionsDAO
Christophe Henry's avatar
Christophe Henry committed
28 29 30 31 32

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

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

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

    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)
47
        }
48 49 50 51 52 53 54
        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)
                }
55
        }
56 57 58 59 60 61 62 63
        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()

64
    fun updateSubscriptionCount(id: String, count: Int) = getSubscriptionsDAO().updateCount(id, count)
65 66
    fun updateSubscriptionNewestArticleDate(id: String, newestArticleDate: LocalDateTime) =
        getSubscriptionsDAO().updateNewestArticleDate(id, newestArticleDate)
67 68
    fun incrementSubscriptionCount(id: String) = getSubscriptionsDAO().incrementCount(id)
    fun decrementSubscriptionCount(id: String) = getSubscriptionsDAO().decrementCount(id)
69
    fun insertSubscriptionImage(id: String, bitmap: Bitmap) = getSubscriptionsDAO().insertImage(id, bitmap)
70
    fun getSubcriptionsById(id: String) = getSubscriptionsDAO().byId(id)
71
    fun getAllSubcriptions() = getSubscriptionsDAO().getAll()
72
    fun getAllUnreadSubcriptions() = getSubscriptionsDAO().getAllUnread()
73
    fun getAllSubcriptionsIds() = getSubscriptionsDAO().getAllIds()
74
    fun getAllSubcriptionsWithImageToUpdate() = getSubscriptionsDAO().withImageToUpdate()
75 76 77 78 79 80 81

    companion object {
        private val dbName by lazy {
            "${FreshRSSApplication.context.getString(R.string.app_name).toLowerCase()}.db"
        }
        val instance by lazy {
            val instance =
82 83
                Room
                    .databaseBuilder(FreshRSSApplication.context, FreshRSSDabatabase::class.java, dbName)
84
                    .addMigrations(*FreshRSSDabatabase_Migrations.build())
Christophe Henry's avatar
Christophe Henry committed
85
                    .build()
86 87 88 89 90 91 92 93 94 95 96 97 98 99
            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
100
            task {getAuthTokensDAO().insert(account)}.getOrDefault(Unit)
101 102 103 104 105 106 107 108 109
        }

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