diff --git a/app/schemas/io.heckel.ntfy.db.Database/9.json b/app/schemas/io.heckel.ntfy.db.Database/9.json index 024364b..7f5d193 100644 --- a/app/schemas/io.heckel.ntfy.db.Database/9.json +++ b/app/schemas/io.heckel.ntfy.db.Database/9.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 9, - "identityHash": "5bab75c3b41c53c9855fe3a7ef8f0669", + "identityHash": "9d62f6db149468db28b5e175be3a9669", "entities": [ { "tableName": "Subscription", @@ -82,7 +82,7 @@ }, { "tableName": "Notification", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `subscriptionId` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `title` TEXT NOT NULL, `message` TEXT NOT NULL, `encoding` TEXT NOT NULL, `notificationId` INTEGER NOT NULL, `priority` INTEGER NOT NULL DEFAULT 3, `tags` TEXT NOT NULL, `click` TEXT NOT NULL, `deleted` INTEGER NOT NULL, `attachment_name` TEXT, `attachment_type` TEXT, `attachment_size` INTEGER, `attachment_expires` INTEGER, `attachment_url` TEXT, `attachment_contentUri` TEXT, `attachment_progress` INTEGER, PRIMARY KEY(`id`, `subscriptionId`))", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `subscriptionId` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `updated` INTEGER NOT NULL, `title` TEXT NOT NULL, `message` TEXT NOT NULL, `encoding` TEXT NOT NULL, `notificationId` INTEGER NOT NULL, `priority` INTEGER NOT NULL DEFAULT 3, `tags` TEXT NOT NULL, `click` TEXT NOT NULL, `deleted` INTEGER NOT NULL, `attachment_name` TEXT, `attachment_type` TEXT, `attachment_size` INTEGER, `attachment_expires` INTEGER, `attachment_url` TEXT, `attachment_contentUri` TEXT, `attachment_progress` INTEGER, PRIMARY KEY(`id`, `subscriptionId`))", "fields": [ { "fieldPath": "id", @@ -102,6 +102,12 @@ "affinity": "INTEGER", "notNull": true }, + { + "fieldPath": "updated", + "columnName": "updated", + "affinity": "INTEGER", + "notNull": true + }, { "fieldPath": "title", "columnName": "title", @@ -290,7 +296,7 @@ "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, '5bab75c3b41c53c9855fe3a7ef8f0669')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9d62f6db149468db28b5e175be3a9669')" ] } } \ No newline at end of file diff --git a/app/src/main/java/io/heckel/ntfy/backup/Backuper.kt b/app/src/main/java/io/heckel/ntfy/backup/Backuper.kt index 52720d3..0334c4c 100644 --- a/app/src/main/java/io/heckel/ntfy/backup/Backuper.kt +++ b/app/src/main/java/io/heckel/ntfy/backup/Backuper.kt @@ -122,10 +122,11 @@ class Backuper(val context: Context) { } else { null } - repository.addNotification(io.heckel.ntfy.db.Notification( + repository.upsertNotification(io.heckel.ntfy.db.Notification( id = n.id, subscriptionId = n.subscriptionId, timestamp = n.timestamp, + updated = n.updated ?: 0L, title = n.title, message = n.message, encoding = n.encoding, @@ -218,6 +219,7 @@ class Backuper(val context: Context) { id = n.id, subscriptionId = n.subscriptionId, timestamp = n.timestamp, + updated = n.updated, title = n.title, message = n.message, encoding = n.encoding, @@ -284,6 +286,7 @@ data class Notification( val id: String, val subscriptionId: Long, val timestamp: Long, + val updated: Long?, val title: String, val message: String, val encoding: String, // "base64" or "" diff --git a/app/src/main/java/io/heckel/ntfy/db/Database.kt b/app/src/main/java/io/heckel/ntfy/db/Database.kt index ed3d550..bbcf68e 100644 --- a/app/src/main/java/io/heckel/ntfy/db/Database.kt +++ b/app/src/main/java/io/heckel/ntfy/db/Database.kt @@ -48,6 +48,7 @@ data class Notification( @ColumnInfo(name = "id") val id: String, @ColumnInfo(name = "subscriptionId") val subscriptionId: Long, @ColumnInfo(name = "timestamp") val timestamp: Long, // Unix timestamp + @ColumnInfo(name = "updated") val updated: Long, // Unix timestamp, may be zero @ColumnInfo(name = "title") val title: String, @ColumnInfo(name = "message") val message: String, @ColumnInfo(name = "encoding") val encoding: String, // "base64" or "" diff --git a/app/src/main/java/io/heckel/ntfy/db/Repository.kt b/app/src/main/java/io/heckel/ntfy/db/Repository.kt index c2b2b26..2ad24e7 100644 --- a/app/src/main/java/io/heckel/ntfy/db/Repository.kt +++ b/app/src/main/java/io/heckel/ntfy/db/Repository.kt @@ -109,15 +109,37 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas return notifications.filterNot { existingIds.contains(it.id) } } + /** + * Adds the notification to the database (if it does not exist), or updates it if it does. + * Returns null if the notification was not added/updated, or the added/updated notification if it was. + * + * You should use the returned notification for further processing. + */ @Suppress("RedundantSuspendModifier") - @WorkerThread - suspend fun addNotification(notification: Notification): Boolean { - val maybeExistingNotification = notificationDao.get(notification.id) - if (maybeExistingNotification != null) { - return false + suspend fun upsertNotification(notification: Notification): Notification? { + val existingNotification = notificationDao.get(notification.id) + if (existingNotification != null) { + return maybeUpdateExistingNotification(existingNotification, notification) } notificationDao.add(notification) - return true + return notification + } + + private fun maybeUpdateExistingNotification(existingNotification: Notification, notification: Notification): Notification? { + if (notification.updated == 0L) { + return null + } else if (notification.updated <= existingNotification.updated) { + return null + } + val newNotification = existingNotification.copy( + message = notification.message, + title = notification.title, + tags = notification.tags, + priority = notification.priority, + click = notification.click + ) + notificationDao.update(newNotification) + return newNotification } fun updateNotification(notification: Notification) { diff --git a/app/src/main/java/io/heckel/ntfy/msg/Message.kt b/app/src/main/java/io/heckel/ntfy/msg/Message.kt index 51ed150..98ea0b7 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/Message.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/Message.kt @@ -8,6 +8,7 @@ import androidx.annotation.Keep data class Message( val id: String, val time: Long, + val updated: Long?, val event: String, val topic: String, val priority: Int?, diff --git a/app/src/main/java/io/heckel/ntfy/msg/NotificationParser.kt b/app/src/main/java/io/heckel/ntfy/msg/NotificationParser.kt index 48b873b..7dba1ab 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/NotificationParser.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/NotificationParser.kt @@ -33,6 +33,7 @@ class NotificationParser { id = message.id, subscriptionId = subscriptionId, timestamp = message.time, + updated = message.updated ?: 0, title = message.title ?: "", message = message.message, encoding = message.encoding ?: "", diff --git a/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt b/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt index 42aea96..e113805 100644 --- a/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt +++ b/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt @@ -261,9 +261,10 @@ class SubscriberService : Service() { val url = topicUrl(subscription.baseUrl, subscription.topic) Log.d(TAG, "[$url] Received notification: $notification") GlobalScope.launch(Dispatchers.IO) { - if (repository.addNotification(notification)) { - Log.d(TAG, "[$url] Dispatching notification $notification") - dispatcher.dispatch(subscription, notification) + val maybeUpdatedNotification = repository.upsertNotification(notification) + if (maybeUpdatedNotification != null) { + Log.d(TAG, "[$url] Dispatching notification $maybeUpdatedNotification") + dispatcher.dispatch(subscription, maybeUpdatedNotification) } wakeLock?.let { if (it.isHeld) { diff --git a/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt index cad7da5..785be0a 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt @@ -8,7 +8,6 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import android.text.Html -import android.util.Base64 import android.view.ActionMode import android.view.Menu import android.view.MenuItem @@ -31,7 +30,6 @@ import io.heckel.ntfy.db.Repository import io.heckel.ntfy.firebase.FirebaseMessenger import io.heckel.ntfy.util.Log import io.heckel.ntfy.msg.ApiService -import io.heckel.ntfy.msg.MESSAGE_ENCODING_BASE64 import io.heckel.ntfy.msg.NotificationService import io.heckel.ntfy.service.SubscriberServiceManager import io.heckel.ntfy.util.* @@ -381,7 +379,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra } else { getString(R.string.refresh_message_result, newNotifications.size) } - newNotifications.forEach { notification -> repository.addNotification(notification) } + newNotifications.forEach { notification -> repository.upsertNotification(notification) } runOnUiThread { Toast.makeText(this@DetailActivity, toastMessage, Toast.LENGTH_LONG).show() mainListContainer.isRefreshing = false diff --git a/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt index cf6e554..470acca 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt @@ -444,7 +444,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc try { val user = repository.getUser(subscription.baseUrl) // May be null val notifications = api.poll(subscription.id, subscription.baseUrl, subscription.topic, user) - notifications.forEach { notification -> repository.addNotification(notification) } + notifications.forEach { notification -> repository.upsertNotification(notification) } } catch (e: Exception) { Log.e(TAG, "Unable to fetch notifications: ${e.message}", e) } @@ -492,8 +492,9 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc newNotifications.forEach { notification -> newNotificationsCount++ val notificationWithId = notification.copy(notificationId = Random.nextInt()) - if (repository.addNotification(notificationWithId)) { - dispatcher?.dispatch(subscription, notificationWithId) + val maybeUpdatedNotification = repository.upsertNotification(notificationWithId) + if (maybeUpdatedNotification != null) { + dispatcher?.dispatch(subscription, maybeUpdatedNotification) } } } catch (e: Exception) { diff --git a/app/src/main/java/io/heckel/ntfy/work/PollWorker.kt b/app/src/main/java/io/heckel/ntfy/work/PollWorker.kt index 90a1e0e..49d7556 100644 --- a/app/src/main/java/io/heckel/ntfy/work/PollWorker.kt +++ b/app/src/main/java/io/heckel/ntfy/work/PollWorker.kt @@ -51,8 +51,9 @@ class PollWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, .onlyNewNotifications(subscription.id, notifications) .map { it.copy(notificationId = Random.nextInt()) } newNotifications.forEach { notification -> - if (repository.addNotification(notification)) { - dispatcher.dispatch(subscription, notification) + val maybeUpdatedNotification = repository.upsertNotification(notification) + if (maybeUpdatedNotification != null) { + dispatcher.dispatch(subscription, maybeUpdatedNotification) } } } catch (e: Exception) { diff --git a/app/src/play/java/io/heckel/ntfy/firebase/FirebaseService.kt b/app/src/play/java/io/heckel/ntfy/firebase/FirebaseService.kt index 2506cee..a4c83bf 100644 --- a/app/src/play/java/io/heckel/ntfy/firebase/FirebaseService.kt +++ b/app/src/play/java/io/heckel/ntfy/firebase/FirebaseService.kt @@ -1,7 +1,6 @@ package io.heckel.ntfy.firebase import android.content.Intent -import android.util.Base64 import androidx.work.* import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage @@ -11,7 +10,6 @@ import io.heckel.ntfy.db.Attachment import io.heckel.ntfy.db.Notification import io.heckel.ntfy.util.Log import io.heckel.ntfy.msg.ApiService -import io.heckel.ntfy.msg.MESSAGE_ENCODING_BASE64 import io.heckel.ntfy.msg.NotificationDispatcher import io.heckel.ntfy.service.SubscriberService import io.heckel.ntfy.util.toPriority @@ -82,6 +80,7 @@ class FirebaseService : FirebaseMessagingService() { val data = remoteMessage.data val id = data["id"] val timestamp = data["time"]?.toLongOrNull() + val updated = data["updated"]?.toLongOrNull() val topic = data["topic"] val title = data["title"] val message = data["message"] @@ -125,6 +124,7 @@ class FirebaseService : FirebaseMessagingService() { id = id, subscriptionId = subscription.id, timestamp = timestamp, + updated = updated ?: 0L, title = title ?: "", message = message, encoding = encoding ?: "", @@ -135,9 +135,10 @@ class FirebaseService : FirebaseMessagingService() { notificationId = Random.nextInt(), deleted = false ) - if (repository.addNotification(notification)) { + val maybeUpdatedNotification = repository.upsertNotification(notification) + if (maybeUpdatedNotification != null) { Log.d(TAG, "Dispatching notification for message: from=${remoteMessage.from}, fcmprio=${remoteMessage.priority}, fcmprio_orig=${remoteMessage.originalPriority}, data=${data}") - dispatcher.dispatch(subscription, notification) + dispatcher.dispatch(subscription, maybeUpdatedNotification) } } }