1
0
mirror of https://github.com/AllanWang/Frost-for-Facebook.git synced 2024-11-08 20:12:39 +01:00

Wrap all db calls using our own context

This commit is contained in:
Allan Wang 2019-04-21 21:02:55 -04:00
parent 4739e6f58d
commit 5002792f2c
No known key found for this signature in database
GPG Key ID: C93E3F9C679D7A56
12 changed files with 78 additions and 141 deletions

View File

@ -38,7 +38,9 @@ import com.pitchedapps.frost.db.CookieModel
import com.pitchedapps.frost.db.FbTabModel
import com.pitchedapps.frost.db.GenericDao
import com.pitchedapps.frost.db.getTabs
import com.pitchedapps.frost.db.save
import com.pitchedapps.frost.db.saveTabs
import com.pitchedapps.frost.db.selectAll
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.utils.EXTRA_COOKIES
import com.pitchedapps.frost.utils.L

View File

@ -34,6 +34,8 @@ import com.bumptech.glide.request.target.Target
import com.pitchedapps.frost.R
import com.pitchedapps.frost.db.CookieDao
import com.pitchedapps.frost.db.CookieEntity
import com.pitchedapps.frost.db.save
import com.pitchedapps.frost.db.selectAll
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.facebook.profilePictureUrl

View File

@ -55,23 +55,32 @@ data class CacheEntity(
interface CacheDao {
@Query("SELECT * FROM frost_cache WHERE id = :id AND type = :type")
suspend fun select(id: Long, type: String): CacheEntity?
fun _select(id: Long, type: String): CacheEntity?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertCache(cache: CacheEntity)
fun _insertCache(cache: CacheEntity)
@Query("DELETE FROM frost_cache WHERE id = :id AND type = :type")
suspend fun delete(id: Long, type: String)
fun _delete(id: Long, type: String)
}
suspend fun CacheDao.select(id: Long, type: String) = dao {
_select(id, type)
}
suspend fun CacheDao.delete(id: Long, type: String) = dao {
_delete(id, type)
}
/**
* Returns true if successful, given that there are constraints to the insertion
*/
suspend fun CacheDao.save(id: Long, type: String, contents: String): Boolean =
suspend fun CacheDao.save(id: Long, type: String, contents: String): Boolean = dao {
try {
insertCache(CacheEntity(id, type, System.currentTimeMillis(), contents))
_insertCache(CacheEntity(id, type, System.currentTimeMillis(), contents))
true
} catch (e: Exception) {
L.e(e) { "Cache save failed for $type" }
false
}
}

View File

@ -53,21 +53,26 @@ data class CookieEntity(
interface CookieDao {
@Query("SELECT * FROM cookies")
suspend fun selectAll(): List<CookieEntity>
fun _selectAll(): List<CookieEntity>
@Query("SELECT * FROM cookies WHERE cookie_id = :id")
suspend fun selectById(id: Long): CookieEntity?
fun _selectById(id: Long): CookieEntity?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun save(cookie: CookieEntity)
fun _save(cookie: CookieEntity)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun save(cookies: List<CookieEntity>)
fun _save(cookies: List<CookieEntity>)
@Query("DELETE FROM cookies WHERE cookie_id = :id")
suspend fun deleteById(id: Long)
fun _deleteById(id: Long)
}
suspend fun CookieDao.selectAll() = dao { _selectAll() }
suspend fun CookieDao.selectById(id: Long) = dao { _selectById(id) }
suspend fun CookieDao.save(cookie: CookieEntity) = dao { _save(cookie) }
suspend fun CookieDao.save(cookies: List<CookieEntity>) = dao { _save(cookies) }
suspend fun CookieDao.deleteById(id: Long) = dao { _deleteById(id) }
suspend fun CookieDao.currentCookie() = selectById(Prefs.userId)
@Database(version = CookiesDb.VERSION)

View File

@ -0,0 +1,28 @@
/*
* Copyright 2019 Allan Wang
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.pitchedapps.frost.db
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/**
* Wraps dao calls to work with coroutines
* Non transactional queries were supposed to be fixed in https://issuetracker.google.com/issues/69474692,
* but it still requires dispatch from a non ui thread.
* This avoids that constraint
*/
suspend inline fun <T> dao(crossinline block: () -> T) = withContext(Dispatchers.IO) { block() }

View File

@ -43,27 +43,27 @@ data class GenericEntity(
interface GenericDao {
@Query("SELECT contents FROM frost_generic WHERE type = :type")
suspend fun select(type: String): String?
fun _select(type: String): String?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun save(entity: GenericEntity)
fun _save(entity: GenericEntity)
@Query("DELETE FROM frost_generic WHERE type = :type")
suspend fun delete(type: String)
fun _delete(type: String)
companion object {
const val TYPE_TABS = "generic_tabs"
}
}
suspend fun GenericDao.saveTabs(tabs: List<FbItem>) {
suspend fun GenericDao.saveTabs(tabs: List<FbItem>) = dao {
val content = tabs.joinToString(",") { it.name }
save(GenericEntity(GenericDao.TYPE_TABS, content))
_save(GenericEntity(GenericDao.TYPE_TABS, content))
}
suspend fun GenericDao.getTabs(): List<FbItem> {
suspend fun GenericDao.getTabs(): List<FbItem> = dao {
val allTabs = FbItem.values.map { it.name to it }.toMap()
return select(GenericDao.TYPE_TABS)
_select(GenericDao.TYPE_TABS)
?.split(",")
?.mapNotNull { allTabs[it] }
?.takeIf { it.isNotEmpty() }

View File

@ -42,8 +42,6 @@ import com.raizlabs.android.dbflow.kotlinextensions.where
import com.raizlabs.android.dbflow.sql.SQLiteType
import com.raizlabs.android.dbflow.sql.migration.AlterTableMigration
import com.raizlabs.android.dbflow.structure.BaseModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Entity(
tableName = "notifications",
@ -120,7 +118,7 @@ interface NotificationDao {
fun _deleteNotifications(userId: Long, type: String)
@Query("DELETE FROM notifications")
suspend fun deleteAll()
fun _deleteAll()
/**
* It is assumed that the notification batch comes from the same user
@ -134,17 +132,18 @@ interface NotificationDao {
}
}
suspend fun NotificationDao.selectNotifications(userId: Long, type: String): List<NotificationContent> =
withContext(Dispatchers.IO) {
_selectNotifications(userId, type).map { it.toNotifContent() }
}
suspend fun NotificationDao.deleteAll() = dao { _deleteAll() }
suspend fun NotificationDao.selectNotifications(userId: Long, type: String): List<NotificationContent> = dao {
_selectNotifications(userId, type).map { it.toNotifContent() }
}
/**
* Returns true if successful, given that there are constraints to the insertion
*/
suspend fun NotificationDao.saveNotifications(type: String, notifs: List<NotificationContent>): Boolean {
if (notifs.isEmpty()) return true
return withContext(Dispatchers.IO) {
return dao {
try {
_saveNotifications(type, notifs)
true
@ -155,7 +154,7 @@ suspend fun NotificationDao.saveNotifications(type: String, notifs: List<Notific
}
}
suspend fun NotificationDao.latestEpoch(userId: Long, type: String): Long = withContext(Dispatchers.IO) {
suspend fun NotificationDao.latestEpoch(userId: Long, type: String): Long = dao {
_selectEpoch(userId, type) ?: lastNotificationTime(userId).let {
when (type) {
NOTIF_CHANNEL_GENERAL -> it.epoch

View File

@ -1,115 +0,0 @@
/*
* Copyright 2018 Allan Wang
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.pitchedapps.frost.db
import androidx.room.Dao
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import com.pitchedapps.frost.db.CookieModel_Table.cookie
import com.pitchedapps.frost.facebook.parsers.FrostThread
import com.pitchedapps.frost.utils.L
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Entity(
tableName = "threads",
primaryKeys = ["thread_id", "userId"],
foreignKeys = [ForeignKey(
entity = CookieEntity::class,
parentColumns = ["cookie_id"],
childColumns = ["userId"],
onDelete = ForeignKey.CASCADE
)],
indices = [Index("thread_id"), Index("userId")]
)
data class ThreadEntity(
@Embedded(prefix = "thread_")
val thread: FrostThread,
val userId: Long
)
data class ThreadContentEntity(
@Embedded
val cookie: CookieEntity,
@Embedded(prefix = "thread_")
val thread: FrostThread
)
@Dao
interface ThreadDao {
/**
* Note that notifications are guaranteed to be ordered by descending timestamp
*/
@Transaction
@Query("SELECT * FROM cookies INNER JOIN threads ON cookie_id = userId WHERE userId = :userId ORDER BY thread_time DESC")
fun _selectThreads(userId: Long): List<ThreadContentEntity>
@Query("SELECT thread_time FROM threads WHERE userId = :userId ORDER BY thread_time DESC LIMIT 1")
fun _selectEpoch(userId: Long): Long?
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun _insertThreads(notifs: List<ThreadEntity>)
@Query("DELETE FROM threads WHERE userId = :userId ")
fun _deleteThreads(userId: Long)
@Query("DELETE FROM threads")
suspend fun deleteAll()
/**
* It is assumed that the notification batch comes from the same user
*/
@Transaction
fun _saveThreads(userId: Long, notifs: List<FrostThread>) {
val entities = notifs.map { ThreadEntity(it, userId) }
_deleteThreads(userId)
_insertThreads(entities)
}
}
suspend fun ThreadDao.selectThreads(userId: Long): List<ThreadContentEntity> =
withContext(Dispatchers.IO) {
_selectThreads(userId)
}
/**
* Returns true if successful, given that there are constraints to the insertion
*/
suspend fun ThreadDao.saveThreads(userId: Long, threads: List<FrostThread>): Boolean {
if (threads.isEmpty()) return true
return withContext(Dispatchers.IO) {
try {
_saveThreads(userId, threads)
true
} catch (e: Exception) {
L.e(e) { "Thread save failed" }
false
}
}
}
suspend fun ThreadDao.latestEpoch(userId: Long, type: String): Long =
withContext(Dispatchers.IO) {
_selectEpoch(userId) ?: lastNotificationTime(userId).epochIm
}

View File

@ -22,6 +22,9 @@ import android.webkit.CookieManager
import com.pitchedapps.frost.db.CookieDao
import com.pitchedapps.frost.db.CookieEntity
import com.pitchedapps.frost.db.FrostDatabase
import com.pitchedapps.frost.db.deleteById
import com.pitchedapps.frost.db.save
import com.pitchedapps.frost.db.selectById
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.cookies

View File

@ -23,6 +23,7 @@ import com.pitchedapps.frost.BuildConfig
import com.pitchedapps.frost.R
import com.pitchedapps.frost.db.CookieDao
import com.pitchedapps.frost.db.CookieEntity
import com.pitchedapps.frost.db.selectAll
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.frostEvent

View File

@ -30,6 +30,7 @@ import com.pitchedapps.frost.BuildConfig
import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.db.FrostDatabase
import com.pitchedapps.frost.db.deleteAll
import com.pitchedapps.frost.services.fetchNotifications
import com.pitchedapps.frost.services.scheduleNotifications
import com.pitchedapps.frost.utils.Prefs

View File

@ -58,6 +58,7 @@ class DebugWebView @JvmOverloads constructor(
settings.userAgentString = USER_AGENT_MOBILE
setLayerType(View.LAYER_TYPE_HARDWARE, null)
webViewClient = DebugClient()
@Suppress("DEPRECATION")
isDrawingCacheEnabled = true
}
@ -72,6 +73,7 @@ class DebugWebView @JvmOverloads constructor(
}
try {
output.outputStream().use {
@Suppress("DEPRECATION")
drawingCache.compress(Bitmap.CompressFormat.PNG, 100, it)
}
L.d { "Created screenshot at ${output.absolutePath}" }