Commit 6c5adcfe authored by Christophe Henry's avatar Christophe Henry
Browse files

Fix 94: prevent refresh from failing when feed icon contains errors

parent f33be6a1
Pipeline #4168 passed with stage
in 0 seconds
# Development
* Fix [!96](https://git.feneas.org/christophehenry/freshrss-android/-/issues/96): App crashes when trying to login to a bad URL ([!98](https://git.feneas.org/christophehenry/freshrss-android/-/merge_requests/98))
* Fix [!98](https://git.feneas.org/christophehenry/freshrss-android/-/issues/98) Automatically refresh if needed on activity resume ([!100](https://git.feneas.org/christophehenry/freshrss-android/merge_requests/100))
# Bug fixes
* Fix [#96](https://git.feneas.org/christophehenry/freshrss-android/-/issues/96): app crashes when trying to login to a bad URL ([!98](https://git.feneas.org/christophehenry/freshrss-android/-/merge_requests/98))
* Fix [#98](https://git.feneas.org/christophehenry/freshrss-android/-/issues/98): automatically refresh if needed on activity resume ([!100](https://git.feneas.org/christophehenry/freshrss-android/merge_requests/100))
* Fix [#94](https://git.feneas.org/christophehenry/freshrss-android/-/issues/94): refresh fails when feed icon contains errors ([!102](https://git.feneas.org/christophehenry/freshrss-android/-/merge_requests/102))
# 1.3.0
......
......@@ -68,6 +68,11 @@ class RefreshWorker(appContext: Context, workerParams: WorkerParameters): Corout
Store.getFavoriteItemIds()
}
// We don't really care about the result of this operation
GlobalScope.launch {
Store.fetchImages()
}
cancellAllNotifications()
val now = DateTime.now(getUserTimeZone())
......
......@@ -86,16 +86,21 @@ object Store {
}
listOf(feedInsertAsync, feedTagRelationInsertAsync).awaitAll()
// TODO: Extract
val asyncs = db().getAllSubcriptionsWithImageToUpdate().map {
async {db().insertSubscriptionImage(it.id, it.fetchImage())}
}
asyncs.awaitAll()
}
}
}
suspend fun fetchImages(): Unit = withContext(Dispatchers.IO) {
db().getAllSubcriptionsWithImageToUpdate().mapNotNull {
val result = it.fetchImage()
if(result.isFailure) null
else async {
SubscriptionImageUpdate(it.id, result.getOrThrow(), it.iconUrl.trim())
.let(db()::insertSubscriptionImage)
}
}.awaitAll()
}.unit()
suspend fun getUnreadCount() {
executeSafeApiCall {getUnreadCount()}.onSuccess {result ->
withContext(Dispatchers.IO) {
......
package fr.chenry.android.freshrss.store.database
import android.graphics.Bitmap
import androidx.room.*
import dev.matrix.roomigrant.GenerateRoomMigrations
import fr.chenry.android.freshrss.store.database.models.*
......@@ -10,7 +9,7 @@ import org.joda.time.DateTime
import org.joda.time.LocalDateTime
@Database(
version = 8,
version = 9,
entities = [
Account::class, Article::class, Subscription::class,
FeedTag::class, FeedToTagRelation::class
......@@ -87,7 +86,7 @@ abstract class FreshRSSDabatabase: RoomDatabase() {
fun incrementSubscriptionCount(id: String) = getSubscriptionsDAO().incrementCount(id)
fun decrementSubscriptionCount(id: String) = getSubscriptionsDAO().decrementCount(id)
fun insertSubscriptionImage(id: String, bitmap: Bitmap) = getSubscriptionsDAO().insertImage(id, bitmap)
fun insertSubscriptionImage(imageUpdate: SubscriptionImageUpdate) = getSubscriptionsDAO().insertImage(imageUpdate)
fun getSubcriptionsById(id: String) = getSubscriptionsDAO().byId(id)
fun getSubcriptionTitleById(id: String) = getSubscriptionsDAO().titleById(id)
fun getSubcriptionsBySubscriptionCategory(feedTag: FeedTag) =
......
{
"formatVersion": 1,
"database": {
"version": 9,
"identityHash": "8855bbc21be629393a28003803d1c7a5",
"entities": [
{
"tableName": "accounts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `SID` TEXT NOT NULL, `Auth` TEXT NOT NULL, `login` TEXT NOT NULL, `serverInstance` TEXT NOT NULL, `unreadCount` INTEGER NOT NULL, `lastFetchDate` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "SID",
"columnName": "SID",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "Auth",
"columnName": "Auth",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "login",
"columnName": "login",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serverInstance",
"columnName": "serverInstance",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "unreadCount",
"columnName": "unreadCount",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "lastFetchDate",
"columnName": "lastFetchDate",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "articles",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `href` TEXT NOT NULL, `author` TEXT NOT NULL, `content` TEXT NOT NULL, `streamId` TEXT NOT NULL, `readStatus` TEXT NOT NULL, `favorite` TEXT NOT NULL, `crawled` INTEGER NOT NULL, `published` INTEGER NOT NULL, `scrollPosition` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "href",
"columnName": "href",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "author",
"columnName": "author",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "streamId",
"columnName": "streamId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "readStatus",
"columnName": "readStatus",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "favorite",
"columnName": "favorite",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "crawled",
"columnName": "crawled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "published",
"columnName": "published",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "scrollPosition",
"columnName": "scrollPosition",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "subscriptions",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`iconUrlFlag` TEXT NOT NULL, `imageBitmap` BLOB, `id` TEXT NOT NULL, `title` TEXT NOT NULL, `iconUrl` TEXT NOT NULL, `unreadCount` INTEGER NOT NULL, `newestArticleDate` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "iconUrlFlag",
"columnName": "iconUrlFlag",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "imageBitmap",
"columnName": "imageBitmap",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "iconUrl",
"columnName": "iconUrl",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "unreadCount",
"columnName": "unreadCount",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "newestArticleDate",
"columnName": "newestArticleDate",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "feed_tags",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `label` TEXT NOT NULL, `type` TEXT NOT NULL, `unreadCount` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "label",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "unreadCount",
"columnName": "unreadCount",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "feed_to_tag_relation",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`feedId` TEXT NOT NULL, `tagId` TEXT NOT NULL, PRIMARY KEY(`feedId`, `tagId`))",
"fields": [
{
"fieldPath": "feedId",
"columnName": "feedId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "tagId",
"columnName": "tagId",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"feedId",
"tagId"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8855bbc21be629393a28003803d1c7a5')"
]
}
}
\ No newline at end of file
......@@ -23,16 +23,18 @@ data class Subscription(
override val unreadCount: Int = 0,
val newestArticleDate: LocalDateTime = LocalDateTime(0)
): Parcelable, FeedLikeDisplayable {
@IgnoredOnParcel
var iconUrlFlag: String = ""
@IgnoredOnParcel
@ColumnInfo(typeAffinity = ColumnInfo.BLOB)
override var imageBitmap: Bitmap? = null
fun fetchImage(): Bitmap =
fun fetchImage(): Result<Bitmap> = runCatching {
Picasso.get()
.load(iconUrl).resizeDimen(R.dimen.icon_size, R.dimen.icon_size)
.get().apply {imageBitmap = this}
.get()
}
fun toSubscriptionUpdate() = SubscriptionUpdate(id, title, iconUrl)
......@@ -40,7 +42,7 @@ data class Subscription(
fun fromSubscriptionApiItem(subscriptionApiItem: SubscriptionApiItem) = Subscription(
subscriptionApiItem.id,
subscriptionApiItem.title.trim(),
subscriptionApiItem.iconUrl
subscriptionApiItem.iconUrl.trim()
)
}
}
......@@ -51,6 +53,16 @@ data class SubscriptionUpdate(
val iconUrl: String
)
data class SubscriptionImageUpdate(
val id: String,
val imageBitmap: Bitmap,
val iconUrlFlag: String
) {
var iconUrl: String = iconUrlFlag.trim()
}
@Dao
interface SubscriptionsDAO {
......@@ -120,7 +132,14 @@ interface SubscriptionsDAO {
)
fun bySubscriptionCategoryAndFavorites(tagId: String): Flowable<Subscriptions>
@Query("SELECT * FROM subscriptions WHERE imageBitmap IS NULL OR LENGTH(imageBitmap) = 0")
@Query(
"""
SELECT * FROM subscriptions
WHERE imageBitmap IS NULL
OR LENGTH(imageBitmap) = 0
OR iconUrl != iconUrlFlag
"""
)
suspend fun withImageToUpdate(): Subscriptions
@Query("DELETE FROM subscriptions WHERE id NOT IN (:ids)")
......@@ -138,8 +157,8 @@ interface SubscriptionsDAO {
@Query("UPDATE subscriptions SET unreadCount = unreadCount - 1 WHERE id = :id AND unreadCount > 0")
fun decrementCount(id: String)
@Query("UPDATE subscriptions SET imageBitmap = :bitmap WHERE id = :id")
fun insertImage(id: String, bitmap: Bitmap)
@Update(entity = Subscription::class)
fun insertImage(imageUpdate: SubscriptionImageUpdate)
@Query("UPDATE subscriptions SET imageBitmap = NULL WHERE id = :id")
fun deleteImage(id: String)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment