From b96c2fb2ed4658d5fa5f86c293d86ec0bfd353bd Mon Sep 17 00:00:00 2001 From: Philipp Heckel Date: Tue, 13 Jan 2026 16:30:57 -0500 Subject: [PATCH] Done --- app/schemas/io.heckel.ntfy.db.Database/18.json | 8 ++++---- app/src/main/java/io/heckel/ntfy/db/Database.kt | 12 ++++++------ app/src/main/java/io/heckel/ntfy/msg/ApiService.kt | 2 +- app/src/main/java/io/heckel/ntfy/msg/Message.kt | 3 +-- .../io/heckel/ntfy/msg/NotificationDispatcher.kt | 2 +- .../java/io/heckel/ntfy/msg/NotificationParser.kt | 4 ++-- app/src/main/java/io/heckel/ntfy/msg/Poller.kt | 4 ++-- .../java/io/heckel/ntfy/service/SubscriberService.kt | 2 +- .../java/io/heckel/ntfy/firebase/FirebaseService.kt | 6 +++--- 9 files changed, 21 insertions(+), 22 deletions(-) diff --git a/app/schemas/io.heckel.ntfy.db.Database/18.json b/app/schemas/io.heckel.ntfy.db.Database/18.json index b6339b5b..1bfa0e9a 100644 --- a/app/schemas/io.heckel.ntfy.db.Database/18.json +++ b/app/schemas/io.heckel.ntfy.db.Database/18.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 18, - "identityHash": "e62fdd1a12610e3514eff4dc83dcc0b8", + "identityHash": "02663facc6503d5ea7015397d5e8cc94", "entities": [ { "tableName": "Subscription", @@ -118,7 +118,7 @@ }, { "tableName": "Notification", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `subscriptionId` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `sequence_id` TEXT NOT NULL, `title` TEXT NOT NULL, `message` TEXT NOT NULL, `contentType` 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, `actions` TEXT, `deleted` INTEGER NOT NULL, `icon_url` TEXT, `icon_contentUri` TEXT, `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, `sequenceId` TEXT NOT NULL, `title` TEXT NOT NULL, `message` TEXT NOT NULL, `contentType` 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, `actions` TEXT, `deleted` INTEGER NOT NULL, `icon_url` TEXT, `icon_contentUri` TEXT, `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", @@ -140,7 +140,7 @@ }, { "fieldPath": "sequenceId", - "columnName": "sequence_id", + "columnName": "sequenceId", "affinity": "TEXT", "notNull": true }, @@ -423,7 +423,7 @@ ], "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, 'e62fdd1a12610e3514eff4dc83dcc0b8')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '02663facc6503d5ea7015397d5e8cc94')" ] } } \ No newline at end of file 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 82dbdaa8..57fa984f 100644 --- a/app/src/main/java/io/heckel/ntfy/db/Database.kt +++ b/app/src/main/java/io/heckel/ntfy/db/Database.kt @@ -146,7 +146,7 @@ data class Notification( @ColumnInfo(name = "id") val id: String, @ColumnInfo(name = "subscriptionId") val subscriptionId: Long, @ColumnInfo(name = "timestamp") val timestamp: Long, // Unix timestamp in seconds - @ColumnInfo(name = "sequence_id") val sequenceId: String, // Sequence ID for updating notifications + @ColumnInfo(name = "sequenceId") val sequenceId: String, // Sequence ID for updating notifications @ColumnInfo(name = "title") val title: String, @ColumnInfo(name = "message") val message: String, @ColumnInfo(name = "contentType") val contentType: String, // "" or "text/markdown" (empty assume text/plain) @@ -159,7 +159,7 @@ data class Notification( @ColumnInfo(name = "actions") val actions: List?, @Embedded(prefix = "attachment_") val attachment: Attachment?, @ColumnInfo(name = "deleted") val deleted: Boolean, - @Ignore val event: String = ApiService.EVENT_MESSAGE, // In-memory event type (message, message_delete, message_read) + @Ignore val event: String = ApiService.EVENT_MESSAGE, // In-memory event type (message, message_delete, message_clear) ) { constructor( id: String, @@ -479,8 +479,8 @@ abstract class Database : RoomDatabase() { private val MIGRATION_17_18 = object : Migration(17, 18) { override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Notification ADD COLUMN sequence_id TEXT NOT NULL DEFAULT ''") - db.execSQL("UPDATE Notification SET sequence_id = id WHERE sequence_id = ''") + db.execSQL("ALTER TABLE Notification ADD COLUMN sequenceId TEXT NOT NULL DEFAULT ''") + db.execSQL("UPDATE Notification SET sequenceId = id WHERE sequenceId = ''") } } } @@ -595,13 +595,13 @@ interface NotificationDao { @Query("UPDATE notification SET notificationId = 0 WHERE subscriptionId = :subscriptionId") fun markAllAsRead(subscriptionId: Long) - @Query("UPDATE notification SET notificationId = 0 WHERE subscriptionId = :subscriptionId AND sequence_id = :sequenceId") + @Query("UPDATE notification SET notificationId = 0 WHERE subscriptionId = :subscriptionId AND sequenceId = :sequenceId") fun markAsReadBySequenceId(subscriptionId: Long, sequenceId: String) @Query("UPDATE notification SET deleted = 1 WHERE id = :notificationId") fun markAsDeleted(notificationId: String) - @Query("UPDATE notification SET deleted = 1 WHERE subscriptionId = :subscriptionId AND sequence_id = :sequenceId") + @Query("UPDATE notification SET deleted = 1 WHERE subscriptionId = :subscriptionId AND sequenceId = :sequenceId") fun markAsDeletedBySequenceId(subscriptionId: Long, sequenceId: String) @Query("UPDATE notification SET deleted = 1 WHERE subscriptionId = :subscriptionId") diff --git a/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt b/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt index 9437cea0..8970b4ef 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt @@ -198,7 +198,7 @@ class ApiService(private val context: Context) { const val CONTROL_TOPIC = "~control" const val EVENT_MESSAGE = "message" const val EVENT_MESSAGE_DELETE = "message_delete" - const val EVENT_MESSAGE_READ = "message_read" + const val EVENT_MESSAGE_CLEAR = "message_clear" const val EVENT_KEEPALIVE = "keepalive" const val EVENT_POLL_REQUEST = "poll_request" } 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 861107db..8aa62b5f 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/Message.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/Message.kt @@ -10,7 +10,6 @@ data class Message( val id: String, val time: Long, @SerializedName("sequence_id") val sequenceId: String?, // Sequence ID for updating notifications - val deleted: Boolean?, // true if the notification sequence is deleted val event: String, val topic: String, val priority: Int?, @@ -19,7 +18,7 @@ data class Message( val icon: String?, val actions: List?, val title: String?, - val message: String, + val message: String?, @SerializedName("content_type") val contentType: String?, val encoding: String?, val attachment: MessageAttachment?, diff --git a/app/src/main/java/io/heckel/ntfy/msg/NotificationDispatcher.kt b/app/src/main/java/io/heckel/ntfy/msg/NotificationDispatcher.kt index f8e9bfba..1c309355 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/NotificationDispatcher.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/NotificationDispatcher.kt @@ -79,7 +79,7 @@ class NotificationDispatcher(val context: Context, val repository: Repository) { } private fun shouldCancel(notification: Notification): Boolean { - return notification.event == ApiService.EVENT_MESSAGE_READ || notification.event == ApiService.EVENT_MESSAGE_DELETE + return notification.event == ApiService.EVENT_MESSAGE_CLEAR || notification.event == ApiService.EVENT_MESSAGE_DELETE } private fun shouldNotify(subscription: Subscription, notification: Notification, muted: Boolean): Boolean { 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 bc5eb361..0eb11808 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/NotificationParser.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/NotificationParser.kt @@ -23,7 +23,7 @@ class NotificationParser { val message = gson.fromJson(s, Message::class.java) val validEvent = message.event == ApiService.EVENT_MESSAGE || message.event == ApiService.EVENT_MESSAGE_DELETE || - message.event == ApiService.EVENT_MESSAGE_READ + message.event == ApiService.EVENT_MESSAGE_CLEAR if (!validEvent) { return null } @@ -60,7 +60,7 @@ class NotificationParser { timestamp = message.time, sequenceId = sequenceId, title = message.title ?: "", - message = message.message, + message = message.message ?: "", contentType = message.contentType ?: "", encoding = message.encoding ?: "", priority = toPriority(message.priority), diff --git a/app/src/main/java/io/heckel/ntfy/msg/Poller.kt b/app/src/main/java/io/heckel/ntfy/msg/Poller.kt index 1828b57c..5fe32111 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/Poller.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/Poller.kt @@ -60,7 +60,7 @@ class Poller( // Handle delete and read events latestBySequenceId - .filter { it.event == ApiService.EVENT_MESSAGE_READ || it.event == ApiService.EVENT_MESSAGE_DELETE } + .filter { it.event == ApiService.EVENT_MESSAGE_CLEAR || it.event == ApiService.EVENT_MESSAGE_DELETE } .forEach { notification -> val sequenceId = notification.sequenceId.ifEmpty { notification.id } when (notification.event) { @@ -68,7 +68,7 @@ class Poller( Log.d(TAG, "Deleting notifications with sequenceId $sequenceId") repository.markAsDeletedBySequenceId(subscriptionId, sequenceId) } - ApiService.EVENT_MESSAGE_READ -> { + ApiService.EVENT_MESSAGE_CLEAR -> { Log.d(TAG, "Marking notifications as read with sequenceId $sequenceId") repository.markAsReadBySequenceId(subscriptionId, sequenceId) } 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 960103fe..0325d5e2 100644 --- a/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt +++ b/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt @@ -329,7 +329,7 @@ class SubscriberService : Service() { // and the web app hooks.js:handleNotification(). when (notification.event) { - ApiService.EVENT_MESSAGE_READ -> { + ApiService.EVENT_MESSAGE_CLEAR -> { if (notification.sequenceId.isNotEmpty()) { repository.markAsReadBySequenceId(subscription.id, notification.sequenceId) } 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 9f30fb99..ff48a04c 100644 --- a/app/src/play/java/io/heckel/ntfy/firebase/FirebaseService.kt +++ b/app/src/play/java/io/heckel/ntfy/firebase/FirebaseService.kt @@ -51,7 +51,7 @@ class FirebaseService : FirebaseMessagingService() { when (data["event"]) { ApiService.EVENT_MESSAGE -> handleMessage(remoteMessage) ApiService.EVENT_MESSAGE_DELETE -> handleMessageDelete(remoteMessage) - ApiService.EVENT_MESSAGE_READ -> handleMessageRead(remoteMessage) + ApiService.EVENT_MESSAGE_CLEAR -> handleMessageClear(remoteMessage) ApiService.EVENT_KEEPALIVE -> handleKeepalive(remoteMessage) ApiService.EVENT_POLL_REQUEST -> handlePollRequest(remoteMessage) else -> Log.d(TAG, "Discarding unexpected message (2): from=${remoteMessage.from}, data=${data}") @@ -108,11 +108,11 @@ class FirebaseService : FirebaseMessagingService() { } } - private fun handleMessageRead(remoteMessage: RemoteMessage) { + private fun handleMessageClear(remoteMessage: RemoteMessage) { val data = remoteMessage.data val topic = data["topic"] ?: return val sequenceId = data["sequence_id"] ?: return - Log.d(TAG, "Received message_read: from=${remoteMessage.from}, topic=$topic, sequenceId=$sequenceId") + Log.d(TAG, "Received message_clear: from=${remoteMessage.from}, topic=$topic, sequenceId=$sequenceId") CoroutineScope(job).launch { val baseUrl = getString(R.string.app_base_url)