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:
parent
4739e6f58d
commit
5002792f2c
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
28
app/src/main/kotlin/com/pitchedapps/frost/db/DaoUtils.kt
Normal file
28
app/src/main/kotlin/com/pitchedapps/frost/db/DaoUtils.kt
Normal 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() }
|
@ -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() }
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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}" }
|
||||
|
Loading…
Reference in New Issue
Block a user