Compare commits
1 commit
main
...
update-mes
Author | SHA1 | Date | |
---|---|---|---|
|
08d362c35c |
11 changed files with 61 additions and 25 deletions
|
@ -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')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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 ""
|
||||||
|
|
|
@ -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 ""
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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?,
|
||||||
|
|
|
@ -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 ?: "",
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue