From 2607454efa3c0789d889fcbaff52bbc54ad98bd0 Mon Sep 17 00:00:00 2001 From: Philipp Heckel Date: Thu, 8 Jan 2026 13:45:25 -0500 Subject: [PATCH] Cancel notifications --- .../java/io/heckel/ntfy/msg/ApiService.kt | 17 ++++++++++++--- .../heckel/ntfy/service/SubscriberService.kt | 11 +++++++++- .../heckel/ntfy/firebase/FirebaseService.kt | 21 ++++++++++++++++--- 3 files changed, 42 insertions(+), 7 deletions(-) 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 a03e358f..b8cf0f65 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt @@ -8,10 +8,21 @@ import io.heckel.ntfy.db.CustomHeader import io.heckel.ntfy.db.Notification import io.heckel.ntfy.db.Repository import io.heckel.ntfy.db.User -import io.heckel.ntfy.util.* -import okhttp3.* -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import io.heckel.ntfy.util.ALL_PRIORITIES +import io.heckel.ntfy.util.Log +import io.heckel.ntfy.util.PRIORITY_DEFAULT +import io.heckel.ntfy.util.topicUrl +import io.heckel.ntfy.util.topicUrlAuth +import io.heckel.ntfy.util.topicUrlJson +import io.heckel.ntfy.util.topicUrlJsonPoll +import okhttp3.Call +import okhttp3.Callback +import okhttp3.Credentials +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response import java.io.IOException import java.net.URLEncoder import java.nio.charset.StandardCharsets.UTF_8 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 bfda891d..7a8cb52e 100644 --- a/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt +++ b/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt @@ -312,7 +312,16 @@ 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)) { + // Note: This logic is duplicated in the FirebaseService::handleMessage() method + // and the web app hooks.js:handleNotification(). + + // Delete existing notification with same sid, if any + if (notification.sid.isNotEmpty()) { + repository.deleteBySid(subscription.id, notification.sid) + } + // Add notification to database and dispatch to be displayed/canceled + val added = repository.addNotification(notification) + if (added || notification.deleted) { Log.d(TAG, "[$url] Dispatching notification $notification") dispatcher.dispatch(subscription, notification) } 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 4a00199d..ce6b1f35 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,12 @@ package io.heckel.ntfy.firebase import android.content.Intent -import androidx.work.* +import androidx.work.Constraints +import androidx.work.ExistingWorkPolicy +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import androidx.work.workDataOf import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage import io.heckel.ntfy.R @@ -9,11 +14,11 @@ import io.heckel.ntfy.app.Application import io.heckel.ntfy.db.Attachment import io.heckel.ntfy.db.Icon import io.heckel.ntfy.db.Notification -import io.heckel.ntfy.util.Log import io.heckel.ntfy.msg.ApiService import io.heckel.ntfy.msg.NotificationDispatcher import io.heckel.ntfy.msg.NotificationParser import io.heckel.ntfy.service.SubscriberService +import io.heckel.ntfy.util.Log import io.heckel.ntfy.util.deriveNotificationId import io.heckel.ntfy.util.nullIfZero import io.heckel.ntfy.util.toPriority @@ -148,7 +153,17 @@ class FirebaseService : FirebaseMessagingService() { notificationId = deriveNotificationId(actualSid), deleted = deleted ) - if (repository.addNotification(notification)) { + + // Note: This logic is duplicated in the SubscriberService::onNotificationReceived() method + // and the web app hooks.js:handleNotification(). + + // Delete existing notification with same sid, if any + if (notification.sid.isNotEmpty()) { + repository.deleteBySid(subscription.id, notification.sid) + } + // Add notification to database and dispatch to be displayed/canceled + val added = repository.addNotification(notification) + if (added || notification.deleted) { Log.d(TAG, "Dispatching notification: from=${remoteMessage.from}, fcmprio=${remoteMessage.priority}, fcmprio_orig=${remoteMessage.originalPriority}, data=${data}") dispatcher.dispatch(subscription, notification) }