Schedule attachment download in dispatcher, not in NotificationService
This commit is contained in:
parent
be750b603b
commit
d440d0a633
7 changed files with 161 additions and 112 deletions
|
@ -2,7 +2,7 @@
|
||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 6,
|
"version": 6,
|
||||||
"identityHash": "6fd36c6995d3ae734f4ba7c8beaf9a95",
|
"identityHash": "1ab02dd84a7f2655b4fc651574b24240",
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
"tableName": "Subscription",
|
"tableName": "Subscription",
|
||||||
|
@ -80,7 +80,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, `notificationId` INTEGER NOT NULL, `priority` INTEGER NOT NULL DEFAULT 3, `tags` TEXT NOT NULL, `click` TEXT NOT NULL, `attachmentName` TEXT, `attachmentType` TEXT, `attachmentSize` INTEGER, `attachmentExpires` INTEGER, `attachmentPreviewUrl` TEXT, `attachmentUrl` TEXT, `attachmentContentUri` TEXT, `deleted` INTEGER NOT NULL, PRIMARY KEY(`id`, `subscriptionId`))",
|
"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, `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_previewUrl` TEXT, `attachment_url` TEXT, `attachment_contentUri` TEXT, `attachment_previewFile` TEXT, `attachment_progress` INTEGER, PRIMARY KEY(`id`, `subscriptionId`))",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "id",
|
"fieldPath": "id",
|
||||||
|
@ -137,53 +137,65 @@
|
||||||
"affinity": "TEXT",
|
"affinity": "TEXT",
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldPath": "attachmentName",
|
|
||||||
"columnName": "attachmentName",
|
|
||||||
"affinity": "TEXT",
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldPath": "attachmentType",
|
|
||||||
"columnName": "attachmentType",
|
|
||||||
"affinity": "TEXT",
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldPath": "attachmentSize",
|
|
||||||
"columnName": "attachmentSize",
|
|
||||||
"affinity": "INTEGER",
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldPath": "attachmentExpires",
|
|
||||||
"columnName": "attachmentExpires",
|
|
||||||
"affinity": "INTEGER",
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldPath": "attachmentPreviewUrl",
|
|
||||||
"columnName": "attachmentPreviewUrl",
|
|
||||||
"affinity": "TEXT",
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldPath": "attachmentUrl",
|
|
||||||
"columnName": "attachmentUrl",
|
|
||||||
"affinity": "TEXT",
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldPath": "attachmentContentUri",
|
|
||||||
"columnName": "attachmentContentUri",
|
|
||||||
"affinity": "TEXT",
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldPath": "deleted",
|
"fieldPath": "deleted",
|
||||||
"columnName": "deleted",
|
"columnName": "deleted",
|
||||||
"affinity": "INTEGER",
|
"affinity": "INTEGER",
|
||||||
"notNull": true
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachment.name",
|
||||||
|
"columnName": "attachment_name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachment.type",
|
||||||
|
"columnName": "attachment_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachment.size",
|
||||||
|
"columnName": "attachment_size",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachment.expires",
|
||||||
|
"columnName": "attachment_expires",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachment.previewUrl",
|
||||||
|
"columnName": "attachment_previewUrl",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachment.url",
|
||||||
|
"columnName": "attachment_url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachment.contentUri",
|
||||||
|
"columnName": "attachment_contentUri",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachment.previewFile",
|
||||||
|
"columnName": "attachment_previewFile",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachment.progress",
|
||||||
|
"columnName": "attachment_progress",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"primaryKey": {
|
"primaryKey": {
|
||||||
|
@ -200,7 +212,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, '6fd36c6995d3ae734f4ba7c8beaf9a95')"
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1ab02dd84a7f2655b4fc651574b24240')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,6 +15,7 @@ data class Subscription(
|
||||||
@ColumnInfo(name = "mutedUntil") val mutedUntil: Long, // TODO notificationSound, notificationSchedule
|
@ColumnInfo(name = "mutedUntil") val mutedUntil: Long, // TODO notificationSound, notificationSchedule
|
||||||
@ColumnInfo(name = "upAppId") val upAppId: String?, // UnifiedPush application package name
|
@ColumnInfo(name = "upAppId") val upAppId: String?, // UnifiedPush application package name
|
||||||
@ColumnInfo(name = "upConnectorToken") val upConnectorToken: String?, // UnifiedPush connector token
|
@ColumnInfo(name = "upConnectorToken") val upConnectorToken: String?, // UnifiedPush connector token
|
||||||
|
// TODO autoDownloadAttachments, minPriority
|
||||||
@Ignore val totalCount: Int = 0, // Total notifications
|
@Ignore val totalCount: Int = 0, // Total notifications
|
||||||
@Ignore val newCount: Int = 0, // New notifications
|
@Ignore val newCount: Int = 0, // New notifications
|
||||||
@Ignore val lastActive: Long = 0, // Unix timestamp
|
@Ignore val lastActive: Long = 0, // Unix timestamp
|
||||||
|
@ -52,16 +53,26 @@ data class Notification(
|
||||||
@ColumnInfo(name = "priority", defaultValue = "3") val priority: Int, // 1=min, 3=default, 5=max
|
@ColumnInfo(name = "priority", defaultValue = "3") val priority: Int, // 1=min, 3=default, 5=max
|
||||||
@ColumnInfo(name = "tags") val tags: String,
|
@ColumnInfo(name = "tags") val tags: String,
|
||||||
@ColumnInfo(name = "click") val click: String, // URL/intent to open on notification click
|
@ColumnInfo(name = "click") val click: String, // URL/intent to open on notification click
|
||||||
@ColumnInfo(name = "attachmentName") val attachmentName: String?, // Filename
|
@Embedded(prefix = "attachment_") val attachment: Attachment?,
|
||||||
@ColumnInfo(name = "attachmentType") val attachmentType: String?, // MIME type
|
|
||||||
@ColumnInfo(name = "attachmentSize") val attachmentSize: Long?, // Size in bytes
|
|
||||||
@ColumnInfo(name = "attachmentExpires") val attachmentExpires: Long?, // Unix timestamp
|
|
||||||
@ColumnInfo(name = "attachmentPreviewUrl") val attachmentPreviewUrl: String?,
|
|
||||||
@ColumnInfo(name = "attachmentUrl") val attachmentUrl: String?,
|
|
||||||
@ColumnInfo(name = "attachmentContentUri") val attachmentContentUri: String?,
|
|
||||||
@ColumnInfo(name = "deleted") val deleted: Boolean,
|
@ColumnInfo(name = "deleted") val deleted: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class Attachment(
|
||||||
|
@ColumnInfo(name = "name") val name: String?, // Filename
|
||||||
|
@ColumnInfo(name = "type") val type: String?, // MIME type
|
||||||
|
@ColumnInfo(name = "size") val size: Long?, // Size in bytes
|
||||||
|
@ColumnInfo(name = "expires") val expires: Long?, // Unix timestamp
|
||||||
|
@ColumnInfo(name = "previewUrl") val previewUrl: String?,
|
||||||
|
@ColumnInfo(name = "url") val url: String,
|
||||||
|
@ColumnInfo(name = "contentUri") val contentUri: String?,
|
||||||
|
@ColumnInfo(name = "previewFile") val previewFile: String?,
|
||||||
|
@ColumnInfo(name = "progress") val progress: Int,
|
||||||
|
) {
|
||||||
|
constructor(name: String?, type: String?, size: Long?, expires: Long?, previewUrl: String?, url: String) :
|
||||||
|
this(name, type, size, expires, previewUrl, url, null, null, 0)
|
||||||
|
}
|
||||||
|
|
||||||
@androidx.room.Database(entities = [Subscription::class, Notification::class], version = 6)
|
@androidx.room.Database(entities = [Subscription::class, Notification::class], version = 6)
|
||||||
abstract class Database : RoomDatabase() {
|
abstract class Database : RoomDatabase() {
|
||||||
abstract fun subscriptionDao(): SubscriptionDao
|
abstract fun subscriptionDao(): SubscriptionDao
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.util.Log
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import io.heckel.ntfy.BuildConfig
|
import io.heckel.ntfy.BuildConfig
|
||||||
|
import io.heckel.ntfy.data.Attachment
|
||||||
import io.heckel.ntfy.data.Notification
|
import io.heckel.ntfy.data.Notification
|
||||||
import io.heckel.ntfy.util.topicUrl
|
import io.heckel.ntfy.util.topicUrl
|
||||||
import io.heckel.ntfy.util.topicUrlJson
|
import io.heckel.ntfy.util.topicUrlJson
|
||||||
|
@ -112,6 +113,16 @@ class ApiService {
|
||||||
val message = gson.fromJson(line, Message::class.java)
|
val message = gson.fromJson(line, Message::class.java)
|
||||||
if (message.event == EVENT_MESSAGE) {
|
if (message.event == EVENT_MESSAGE) {
|
||||||
val topic = message.topic
|
val topic = message.topic
|
||||||
|
val attachment = if (message.attachment?.url != null) {
|
||||||
|
Attachment(
|
||||||
|
name = message.attachment.name,
|
||||||
|
type = message.attachment.type,
|
||||||
|
size = message.attachment.size,
|
||||||
|
expires = message.attachment.expires,
|
||||||
|
previewUrl = message.attachment.preview_url,
|
||||||
|
url = message.attachment.url,
|
||||||
|
)
|
||||||
|
} else null
|
||||||
val notification = Notification(
|
val notification = Notification(
|
||||||
id = message.id,
|
id = message.id,
|
||||||
subscriptionId = 0, // TO BE SET downstream
|
subscriptionId = 0, // TO BE SET downstream
|
||||||
|
@ -121,13 +132,7 @@ class ApiService {
|
||||||
priority = toPriority(message.priority),
|
priority = toPriority(message.priority),
|
||||||
tags = joinTags(message.tags),
|
tags = joinTags(message.tags),
|
||||||
click = message.click ?: "",
|
click = message.click ?: "",
|
||||||
attachmentName = message.attachment?.name,
|
attachment = attachment,
|
||||||
attachmentType = message.attachment?.type,
|
|
||||||
attachmentSize = message.attachment?.size,
|
|
||||||
attachmentExpires = message.attachment?.expires,
|
|
||||||
attachmentPreviewUrl = message.attachment?.preview_url,
|
|
||||||
attachmentUrl = message.attachment?.url,
|
|
||||||
attachmentContentUri = null,
|
|
||||||
notificationId = Random.nextInt(),
|
notificationId = Random.nextInt(),
|
||||||
deleted = false
|
deleted = false
|
||||||
)
|
)
|
||||||
|
@ -149,6 +154,16 @@ class ApiService {
|
||||||
|
|
||||||
private fun fromString(subscriptionId: Long, s: String): Notification {
|
private fun fromString(subscriptionId: Long, s: String): Notification {
|
||||||
val message = gson.fromJson(s, Message::class.java)
|
val message = gson.fromJson(s, Message::class.java)
|
||||||
|
val attachment = if (message.attachment?.url != null) {
|
||||||
|
Attachment(
|
||||||
|
name = message.attachment.name,
|
||||||
|
type = message.attachment.type,
|
||||||
|
size = message.attachment.size,
|
||||||
|
expires = message.attachment.expires,
|
||||||
|
previewUrl = message.attachment.preview_url,
|
||||||
|
url = message.attachment.url,
|
||||||
|
)
|
||||||
|
} else null
|
||||||
return Notification(
|
return Notification(
|
||||||
id = message.id,
|
id = message.id,
|
||||||
subscriptionId = subscriptionId,
|
subscriptionId = subscriptionId,
|
||||||
|
@ -158,14 +173,8 @@ class ApiService {
|
||||||
priority = toPriority(message.priority),
|
priority = toPriority(message.priority),
|
||||||
tags = joinTags(message.tags),
|
tags = joinTags(message.tags),
|
||||||
click = message.click ?: "",
|
click = message.click ?: "",
|
||||||
attachmentName = message.attachment?.name,
|
attachment = attachment,
|
||||||
attachmentType = message.attachment?.type,
|
notificationId = 0, // zero!
|
||||||
attachmentSize = message.attachment?.size,
|
|
||||||
attachmentExpires = message.attachment?.expires,
|
|
||||||
attachmentPreviewUrl = message.attachment?.preview_url,
|
|
||||||
attachmentUrl = message.attachment?.url,
|
|
||||||
attachmentContentUri = null,
|
|
||||||
notificationId = 0,
|
|
||||||
deleted = false
|
deleted = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import android.util.Log
|
||||||
import androidx.work.Worker
|
import androidx.work.Worker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import io.heckel.ntfy.app.Application
|
import io.heckel.ntfy.app.Application
|
||||||
|
import io.heckel.ntfy.data.Attachment
|
||||||
import io.heckel.ntfy.data.Notification
|
import io.heckel.ntfy.data.Notification
|
||||||
import io.heckel.ntfy.data.Repository
|
import io.heckel.ntfy.data.Repository
|
||||||
import io.heckel.ntfy.data.Subscription
|
import io.heckel.ntfy.data.Subscription
|
||||||
|
@ -34,15 +35,16 @@ class AttachmentDownloadWorker(private val context: Context, params: WorkerParam
|
||||||
val repository = app.repository
|
val repository = app.repository
|
||||||
val notification = repository.getNotification(notificationId) ?: return Result.failure()
|
val notification = repository.getNotification(notificationId) ?: return Result.failure()
|
||||||
val subscription = repository.getSubscription(notification.subscriptionId) ?: return Result.failure()
|
val subscription = repository.getSubscription(notification.subscriptionId) ?: return Result.failure()
|
||||||
if (notification.attachmentPreviewUrl != null) {
|
val attachment = notification.attachment ?: return Result.failure()
|
||||||
downloadPreview(repository, subscription, notification)
|
if (attachment.previewUrl != null) {
|
||||||
|
downloadPreview(subscription, notification, attachment)
|
||||||
}
|
}
|
||||||
downloadAttachment(repository, subscription, notification)
|
downloadAttachment(repository, subscription, notification, attachment)
|
||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadPreview(repository: Repository, subscription: Subscription, notification: Notification) {
|
private fun downloadPreview(subscription: Subscription, notification: Notification, attachment: Attachment) {
|
||||||
val url = notification.attachmentPreviewUrl ?: return
|
val url = attachment.previewUrl ?: return
|
||||||
Log.d(TAG, "Downloading preview from $url")
|
Log.d(TAG, "Downloading preview from $url")
|
||||||
|
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
|
@ -63,21 +65,20 @@ class AttachmentDownloadWorker(private val context: Context, params: WorkerParam
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadAttachment(repository: Repository, subscription: Subscription, notification: Notification) {
|
private fun downloadAttachment(repository: Repository, subscription: Subscription, notification: Notification, attachment: Attachment) {
|
||||||
val url = notification.attachmentUrl ?: return
|
Log.d(TAG, "Downloading attachment from ${attachment.url}")
|
||||||
Log.d(TAG, "Downloading attachment from $url")
|
|
||||||
|
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url(url)
|
.url(attachment.url)
|
||||||
.addHeader("User-Agent", ApiService.USER_AGENT)
|
.addHeader("User-Agent", ApiService.USER_AGENT)
|
||||||
.build()
|
.build()
|
||||||
client.newCall(request).execute().use { response ->
|
client.newCall(request).execute().use { response ->
|
||||||
if (!response.isSuccessful || response.body == null) {
|
if (!response.isSuccessful || response.body == null) {
|
||||||
throw Exception("Attachment download failed: ${response.code}")
|
throw Exception("Attachment download failed: ${response.code}")
|
||||||
}
|
}
|
||||||
val name = notification.attachmentName ?: "attachment.bin"
|
val name = attachment.name ?: "attachment.bin"
|
||||||
val mimeType = notification.attachmentType ?: "application/octet-stream"
|
val mimeType = attachment.type ?: "application/octet-stream"
|
||||||
val size = notification.attachmentSize ?: 0
|
val size = attachment.size ?: 0
|
||||||
val resolver = applicationContext.contentResolver
|
val resolver = applicationContext.contentResolver
|
||||||
val details = ContentValues().apply {
|
val details = ContentValues().apply {
|
||||||
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
|
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
|
||||||
|
@ -107,7 +108,8 @@ class AttachmentDownloadWorker(private val context: Context, params: WorkerParam
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Attachment download: successful response, proceeding with download")
|
Log.d(TAG, "Attachment download: successful response, proceeding with download")
|
||||||
val newNotification = notification.copy(attachmentContentUri = uri.toString())
|
val newAttachment = attachment.copy(contentUri = uri.toString())
|
||||||
|
val newNotification = notification.copy(attachment = newAttachment)
|
||||||
repository.updateNotification(newNotification)
|
repository.updateNotification(newNotification)
|
||||||
notifier.update(subscription, newNotification)
|
notifier.update(subscription, newNotification)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package io.heckel.ntfy.msg
|
package io.heckel.ntfy.msg
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.work.OneTimeWorkRequest
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.workDataOf
|
||||||
import io.heckel.ntfy.data.Notification
|
import io.heckel.ntfy.data.Notification
|
||||||
import io.heckel.ntfy.data.Repository
|
import io.heckel.ntfy.data.Repository
|
||||||
import io.heckel.ntfy.data.Subscription
|
import io.heckel.ntfy.data.Subscription
|
||||||
|
@ -25,6 +29,7 @@ class NotificationDispatcher(val context: Context, val repository: Repository) {
|
||||||
val notify = shouldNotify(subscription, notification, muted)
|
val notify = shouldNotify(subscription, notification, muted)
|
||||||
val broadcast = shouldBroadcast(subscription)
|
val broadcast = shouldBroadcast(subscription)
|
||||||
val distribute = shouldDistribute(subscription)
|
val distribute = shouldDistribute(subscription)
|
||||||
|
val download = shouldDownload(subscription, notification)
|
||||||
if (notify) {
|
if (notify) {
|
||||||
notifier.display(subscription, notification)
|
notifier.display(subscription, notification)
|
||||||
}
|
}
|
||||||
|
@ -36,6 +41,16 @@ class NotificationDispatcher(val context: Context, val repository: Repository) {
|
||||||
distributor.sendMessage(appId, connectorToken, notification.message)
|
distributor.sendMessage(appId, connectorToken, notification.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (download) {
|
||||||
|
// Download attachment (+ preview if available) in the background via WorkManager
|
||||||
|
// The indirection via WorkManager is required since this code may be executed
|
||||||
|
// in a doze state and Internet may not be available. It's also best practice apparently.
|
||||||
|
scheduleAttachmentDownload(notification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shouldDownload(subscription: Subscription, notification: Notification): Boolean {
|
||||||
|
return notification.attachment != null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shouldNotify(subscription: Subscription, notification: Notification, muted: Boolean): Boolean {
|
private fun shouldNotify(subscription: Subscription, notification: Notification, muted: Boolean): Boolean {
|
||||||
|
@ -67,4 +82,19 @@ class NotificationDispatcher(val context: Context, val repository: Repository) {
|
||||||
}
|
}
|
||||||
return subscription.mutedUntil == 1L || (subscription.mutedUntil > 1L && subscription.mutedUntil > System.currentTimeMillis()/1000)
|
return subscription.mutedUntil == 1L || (subscription.mutedUntil > 1L && subscription.mutedUntil > System.currentTimeMillis()/1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun scheduleAttachmentDownload(notification: Notification) {
|
||||||
|
Log.d(TAG, "Enqueuing work to download attachment (+ preview if available)")
|
||||||
|
val workManager = WorkManager.getInstance(context)
|
||||||
|
val workRequest = OneTimeWorkRequest.Builder(AttachmentDownloadWorker::class.java)
|
||||||
|
.setInputData(workDataOf(
|
||||||
|
"id" to notification.id,
|
||||||
|
))
|
||||||
|
.build()
|
||||||
|
workManager.enqueue(workRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "NtfyNotifDispatch"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,16 +32,7 @@ class NotificationService(val context: Context) {
|
||||||
|
|
||||||
fun display(subscription: Subscription, notification: Notification) {
|
fun display(subscription: Subscription, notification: Notification) {
|
||||||
Log.d(TAG, "Displaying notification $notification")
|
Log.d(TAG, "Displaying notification $notification")
|
||||||
|
|
||||||
// Display notification immediately
|
|
||||||
displayInternal(subscription, notification)
|
displayInternal(subscription, notification)
|
||||||
|
|
||||||
// Download attachment (+ preview if available) in the background via WorkManager
|
|
||||||
// The indirection via WorkManager is required since this code may be executed
|
|
||||||
// in a doze state and Internet may not be available. It's also best practice apparently.
|
|
||||||
if (notification.attachmentUrl != null) {
|
|
||||||
scheduleAttachmentDownload(subscription, notification)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun update(subscription: Subscription, notification: Notification, progress: Int = PROGRESS_NONE) {
|
fun update(subscription: Subscription, notification: Notification, progress: Int = PROGRESS_NONE) {
|
||||||
|
@ -133,32 +124,21 @@ class NotificationService(val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun maybeAddOpenAction(notificationBuilder: NotificationCompat.Builder, notification: Notification) {
|
private fun maybeAddOpenAction(notificationBuilder: NotificationCompat.Builder, notification: Notification) {
|
||||||
if (notification.attachmentContentUri != null) {
|
if (notification.attachment?.contentUri != null) {
|
||||||
val contentUri = Uri.parse(notification.attachmentContentUri)
|
val contentUri = Uri.parse(notification.attachment.contentUri)
|
||||||
val openIntent = PendingIntent.getActivity(context, 0, Intent(Intent.ACTION_VIEW, contentUri), 0)
|
val openIntent = PendingIntent.getActivity(context, 0, Intent(Intent.ACTION_VIEW, contentUri), 0)
|
||||||
notificationBuilder.addAction(NotificationCompat.Action.Builder(0, "Open", openIntent).build())
|
notificationBuilder.addAction(NotificationCompat.Action.Builder(0, "Open", openIntent).build())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun maybeAddCopyUrlAction(builder: NotificationCompat.Builder, notification: Notification) {
|
private fun maybeAddCopyUrlAction(builder: NotificationCompat.Builder, notification: Notification) {
|
||||||
if (notification.attachmentUrl != null) {
|
if (notification.attachment?.url != null) {
|
||||||
// XXXXXXXXx
|
// XXXXXXXXx
|
||||||
val copyUrlIntent = PendingIntent.getActivity(context, 0, Intent(Intent.ACTION_VIEW, Uri.parse(notification.attachmentUrl)), 0)
|
val copyUrlIntent = PendingIntent.getActivity(context, 0, Intent(Intent.ACTION_VIEW, Uri.parse(notification.attachment.url)), 0)
|
||||||
builder.addAction(NotificationCompat.Action.Builder(0, "Copy URL", copyUrlIntent).build())
|
builder.addAction(NotificationCompat.Action.Builder(0, "Copy URL", copyUrlIntent).build())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scheduleAttachmentDownload(subscription: Subscription, notification: Notification) {
|
|
||||||
Log.d(TAG, "Enqueuing work to download attachment (+ preview if available)")
|
|
||||||
val workManager = WorkManager.getInstance(context)
|
|
||||||
val workRequest = OneTimeWorkRequest.Builder(AttachmentDownloadWorker::class.java)
|
|
||||||
.setInputData(workDataOf(
|
|
||||||
"id" to notification.id,
|
|
||||||
))
|
|
||||||
.build()
|
|
||||||
workManager.enqueue(workRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun detailActivityIntent(subscription: Subscription): PendingIntent? {
|
private fun detailActivityIntent(subscription: Subscription): PendingIntent? {
|
||||||
val intent = Intent(context, DetailActivity::class.java)
|
val intent = Intent(context, DetailActivity::class.java)
|
||||||
intent.putExtra(MainActivity.EXTRA_SUBSCRIPTION_ID, subscription.id)
|
intent.putExtra(MainActivity.EXTRA_SUBSCRIPTION_ID, subscription.id)
|
||||||
|
@ -223,7 +203,7 @@ class NotificationService(val context: Context) {
|
||||||
const val PROGRESS_NONE = -1
|
const val PROGRESS_NONE = -1
|
||||||
const val PROGRESS_INDETERMINATE = -2
|
const val PROGRESS_INDETERMINATE = -2
|
||||||
|
|
||||||
private const val TAG = "NtfyNotificationService"
|
private const val TAG = "NtfyNotifService"
|
||||||
private const val CHANNEL_ID_MIN = "ntfy-min"
|
private const val CHANNEL_ID_MIN = "ntfy-min"
|
||||||
private const val CHANNEL_ID_LOW = "ntfy-low"
|
private const val CHANNEL_ID_LOW = "ntfy-low"
|
||||||
private const val CHANNEL_ID_DEFAULT = "ntfy"
|
private const val CHANNEL_ID_DEFAULT = "ntfy"
|
||||||
|
|
|
@ -6,6 +6,7 @@ import com.google.firebase.messaging.FirebaseMessagingService
|
||||||
import com.google.firebase.messaging.RemoteMessage
|
import com.google.firebase.messaging.RemoteMessage
|
||||||
import io.heckel.ntfy.R
|
import io.heckel.ntfy.R
|
||||||
import io.heckel.ntfy.app.Application
|
import io.heckel.ntfy.app.Application
|
||||||
|
import io.heckel.ntfy.data.Attachment
|
||||||
import io.heckel.ntfy.data.Notification
|
import io.heckel.ntfy.data.Notification
|
||||||
import io.heckel.ntfy.msg.*
|
import io.heckel.ntfy.msg.*
|
||||||
import io.heckel.ntfy.service.SubscriberService
|
import io.heckel.ntfy.service.SubscriberService
|
||||||
|
@ -81,6 +82,16 @@ class FirebaseService : FirebaseMessagingService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add notification
|
// Add notification
|
||||||
|
val attachment = if (attachmentUrl != null) {
|
||||||
|
Attachment(
|
||||||
|
name = attachmentName,
|
||||||
|
type = attachmentType,
|
||||||
|
size = attachmentSize,
|
||||||
|
expires = attachmentExpires,
|
||||||
|
previewUrl = attachmentPreviewUrl,
|
||||||
|
url = attachmentUrl,
|
||||||
|
)
|
||||||
|
} else null
|
||||||
val notification = Notification(
|
val notification = Notification(
|
||||||
id = id,
|
id = id,
|
||||||
subscriptionId = subscription.id,
|
subscriptionId = subscription.id,
|
||||||
|
@ -90,13 +101,7 @@ class FirebaseService : FirebaseMessagingService() {
|
||||||
priority = toPriority(priority),
|
priority = toPriority(priority),
|
||||||
tags = tags ?: "",
|
tags = tags ?: "",
|
||||||
click = click ?: "",
|
click = click ?: "",
|
||||||
attachmentName = attachmentName,
|
attachment = attachment,
|
||||||
attachmentType = attachmentType,
|
|
||||||
attachmentSize = attachmentSize,
|
|
||||||
attachmentExpires = attachmentExpires,
|
|
||||||
attachmentPreviewUrl = attachmentPreviewUrl,
|
|
||||||
attachmentUrl = attachmentUrl,
|
|
||||||
attachmentContentUri = null,
|
|
||||||
notificationId = Random.nextInt(),
|
notificationId = Random.nextInt(),
|
||||||
deleted = false
|
deleted = false
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue