Compare commits

...

1 commit

Author SHA1 Message Date
Philipp Heckel
08d362c35c WIP: Update messages 2022-03-23 21:52:26 -04:00
11 changed files with 61 additions and 25 deletions

View file

@ -2,7 +2,7 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 9, "version": 9,
"identityHash": "5bab75c3b41c53c9855fe3a7ef8f0669", "identityHash": "9d62f6db149468db28b5e175be3a9669",
"entities": [ "entities": [
{ {
"tableName": "Subscription", "tableName": "Subscription",
@ -82,7 +82,7 @@
}, },
{ {
"tableName": "Notification", "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": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -102,6 +102,12 @@
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": true "notNull": true
}, },
{
"fieldPath": "updated",
"columnName": "updated",
"affinity": "INTEGER",
"notNull": true
},
{ {
"fieldPath": "title", "fieldPath": "title",
"columnName": "title", "columnName": "title",
@ -290,7 +296,7 @@
"views": [], "views": [],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "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')"
] ]
} }
} }

View file

@ -122,10 +122,11 @@ class Backuper(val context: Context) {
} else { } else {
null null
} }
repository.addNotification(io.heckel.ntfy.db.Notification( repository.upsertNotification(io.heckel.ntfy.db.Notification(
id = n.id, id = n.id,
subscriptionId = n.subscriptionId, subscriptionId = n.subscriptionId,
timestamp = n.timestamp, timestamp = n.timestamp,
updated = n.updated ?: 0L,
title = n.title, title = n.title,
message = n.message, message = n.message,
encoding = n.encoding, encoding = n.encoding,
@ -218,6 +219,7 @@ class Backuper(val context: Context) {
id = n.id, id = n.id,
subscriptionId = n.subscriptionId, subscriptionId = n.subscriptionId,
timestamp = n.timestamp, timestamp = n.timestamp,
updated = n.updated,
title = n.title, title = n.title,
message = n.message, message = n.message,
encoding = n.encoding, encoding = n.encoding,
@ -284,6 +286,7 @@ data class Notification(
val id: String, val id: String,
val subscriptionId: Long, val subscriptionId: Long,
val timestamp: Long, val timestamp: Long,
val updated: Long?,
val title: String, val title: String,
val message: String, val message: String,
val encoding: String, // "base64" or "" val encoding: String, // "base64" or ""

View file

@ -48,6 +48,7 @@ data class Notification(
@ColumnInfo(name = "id") val id: String, @ColumnInfo(name = "id") val id: String,
@ColumnInfo(name = "subscriptionId") val subscriptionId: Long, @ColumnInfo(name = "subscriptionId") val subscriptionId: Long,
@ColumnInfo(name = "timestamp") val timestamp: Long, // Unix timestamp @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 = "title") val title: String,
@ColumnInfo(name = "message") val message: String, @ColumnInfo(name = "message") val message: String,
@ColumnInfo(name = "encoding") val encoding: String, // "base64" or "" @ColumnInfo(name = "encoding") val encoding: String, // "base64" or ""

View file

@ -109,15 +109,37 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
return notifications.filterNot { existingIds.contains(it.id) } 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") @Suppress("RedundantSuspendModifier")
@WorkerThread suspend fun upsertNotification(notification: Notification): Notification? {
suspend fun addNotification(notification: Notification): Boolean { val existingNotification = notificationDao.get(notification.id)
val maybeExistingNotification = notificationDao.get(notification.id) if (existingNotification != null) {
if (maybeExistingNotification != null) { return maybeUpdateExistingNotification(existingNotification, notification)
return false
} }
notificationDao.add(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) { fun updateNotification(notification: Notification) {

View file

@ -8,6 +8,7 @@ import androidx.annotation.Keep
data class Message( data class Message(
val id: String, val id: String,
val time: Long, val time: Long,
val updated: Long?,
val event: String, val event: String,
val topic: String, val topic: String,
val priority: Int?, val priority: Int?,

View file

@ -33,6 +33,7 @@ class NotificationParser {
id = message.id, id = message.id,
subscriptionId = subscriptionId, subscriptionId = subscriptionId,
timestamp = message.time, timestamp = message.time,
updated = message.updated ?: 0,
title = message.title ?: "", title = message.title ?: "",
message = message.message, message = message.message,
encoding = message.encoding ?: "", encoding = message.encoding ?: "",

View file

@ -261,9 +261,10 @@ class SubscriberService : Service() {
val url = topicUrl(subscription.baseUrl, subscription.topic) val url = topicUrl(subscription.baseUrl, subscription.topic)
Log.d(TAG, "[$url] Received notification: $notification") Log.d(TAG, "[$url] Received notification: $notification")
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
if (repository.addNotification(notification)) { val maybeUpdatedNotification = repository.upsertNotification(notification)
Log.d(TAG, "[$url] Dispatching notification $notification") if (maybeUpdatedNotification != null) {
dispatcher.dispatch(subscription, notification) Log.d(TAG, "[$url] Dispatching notification $maybeUpdatedNotification")
dispatcher.dispatch(subscription, maybeUpdatedNotification)
} }
wakeLock?.let { wakeLock?.let {
if (it.isHeld) { if (it.isHeld) {

View file

@ -8,7 +8,6 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.Html import android.text.Html
import android.util.Base64
import android.view.ActionMode import android.view.ActionMode
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@ -31,7 +30,6 @@ import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.firebase.FirebaseMessenger import io.heckel.ntfy.firebase.FirebaseMessenger
import io.heckel.ntfy.util.Log import io.heckel.ntfy.util.Log
import io.heckel.ntfy.msg.ApiService import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.msg.MESSAGE_ENCODING_BASE64
import io.heckel.ntfy.msg.NotificationService import io.heckel.ntfy.msg.NotificationService
import io.heckel.ntfy.service.SubscriberServiceManager import io.heckel.ntfy.service.SubscriberServiceManager
import io.heckel.ntfy.util.* import io.heckel.ntfy.util.*
@ -381,7 +379,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
} else { } else {
getString(R.string.refresh_message_result, newNotifications.size) getString(R.string.refresh_message_result, newNotifications.size)
} }
newNotifications.forEach { notification -> repository.addNotification(notification) } newNotifications.forEach { notification -> repository.upsertNotification(notification) }
runOnUiThread { runOnUiThread {
Toast.makeText(this@DetailActivity, toastMessage, Toast.LENGTH_LONG).show() Toast.makeText(this@DetailActivity, toastMessage, Toast.LENGTH_LONG).show()
mainListContainer.isRefreshing = false mainListContainer.isRefreshing = false

View file

@ -444,7 +444,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
try { try {
val user = repository.getUser(subscription.baseUrl) // May be null val user = repository.getUser(subscription.baseUrl) // May be null
val notifications = api.poll(subscription.id, subscription.baseUrl, subscription.topic, user) 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) { } catch (e: Exception) {
Log.e(TAG, "Unable to fetch notifications: ${e.message}", e) Log.e(TAG, "Unable to fetch notifications: ${e.message}", e)
} }
@ -492,8 +492,9 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
newNotifications.forEach { notification -> newNotifications.forEach { notification ->
newNotificationsCount++ newNotificationsCount++
val notificationWithId = notification.copy(notificationId = Random.nextInt()) val notificationWithId = notification.copy(notificationId = Random.nextInt())
if (repository.addNotification(notificationWithId)) { val maybeUpdatedNotification = repository.upsertNotification(notificationWithId)
dispatcher?.dispatch(subscription, notificationWithId) if (maybeUpdatedNotification != null) {
dispatcher?.dispatch(subscription, maybeUpdatedNotification)
} }
} }
} catch (e: Exception) { } catch (e: Exception) {

View file

@ -51,8 +51,9 @@ class PollWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx,
.onlyNewNotifications(subscription.id, notifications) .onlyNewNotifications(subscription.id, notifications)
.map { it.copy(notificationId = Random.nextInt()) } .map { it.copy(notificationId = Random.nextInt()) }
newNotifications.forEach { notification -> newNotifications.forEach { notification ->
if (repository.addNotification(notification)) { val maybeUpdatedNotification = repository.upsertNotification(notification)
dispatcher.dispatch(subscription, notification) if (maybeUpdatedNotification != null) {
dispatcher.dispatch(subscription, maybeUpdatedNotification)
} }
} }
} catch (e: Exception) { } catch (e: Exception) {

View file

@ -1,7 +1,6 @@
package io.heckel.ntfy.firebase package io.heckel.ntfy.firebase
import android.content.Intent import android.content.Intent
import android.util.Base64
import androidx.work.* import androidx.work.*
import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage 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.db.Notification
import io.heckel.ntfy.util.Log import io.heckel.ntfy.util.Log
import io.heckel.ntfy.msg.ApiService import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.msg.MESSAGE_ENCODING_BASE64
import io.heckel.ntfy.msg.NotificationDispatcher import io.heckel.ntfy.msg.NotificationDispatcher
import io.heckel.ntfy.service.SubscriberService import io.heckel.ntfy.service.SubscriberService
import io.heckel.ntfy.util.toPriority import io.heckel.ntfy.util.toPriority
@ -82,6 +80,7 @@ class FirebaseService : FirebaseMessagingService() {
val data = remoteMessage.data val data = remoteMessage.data
val id = data["id"] val id = data["id"]
val timestamp = data["time"]?.toLongOrNull() val timestamp = data["time"]?.toLongOrNull()
val updated = data["updated"]?.toLongOrNull()
val topic = data["topic"] val topic = data["topic"]
val title = data["title"] val title = data["title"]
val message = data["message"] val message = data["message"]
@ -125,6 +124,7 @@ class FirebaseService : FirebaseMessagingService() {
id = id, id = id,
subscriptionId = subscription.id, subscriptionId = subscription.id,
timestamp = timestamp, timestamp = timestamp,
updated = updated ?: 0L,
title = title ?: "", title = title ?: "",
message = message, message = message,
encoding = encoding ?: "", encoding = encoding ?: "",
@ -135,9 +135,10 @@ class FirebaseService : FirebaseMessagingService() {
notificationId = Random.nextInt(), notificationId = Random.nextInt(),
deleted = false 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}") 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)
} }
} }
} }