Preview URL
This commit is contained in:
parent
f2bb3a022b
commit
9a5af648c4
5 changed files with 78 additions and 19 deletions
|
@ -2,7 +2,7 @@
|
||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 5,
|
"version": 5,
|
||||||
"identityHash": "425a0bc96c8aae9d01985b0f4d7579dc",
|
"identityHash": "fd7d1e0ac6ac7d68eb79ffe928dae67a",
|
||||||
"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, `attachmentName` TEXT, `attachmentType` TEXT, `attachmentExpires` INTEGER, `attachmentUrl` 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, `attachmentName` TEXT, `attachmentType` TEXT, `attachmentSize` INTEGER, `attachmentExpires` INTEGER, `attachmentPreviewUrl` TEXT, `attachmentUrl` TEXT, `deleted` INTEGER NOT NULL, PRIMARY KEY(`id`, `subscriptionId`))",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "id",
|
"fieldPath": "id",
|
||||||
|
@ -143,12 +143,24 @@
|
||||||
"affinity": "TEXT",
|
"affinity": "TEXT",
|
||||||
"notNull": false
|
"notNull": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachmentSize",
|
||||||
|
"columnName": "attachmentSize",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldPath": "attachmentExpires",
|
"fieldPath": "attachmentExpires",
|
||||||
"columnName": "attachmentExpires",
|
"columnName": "attachmentExpires",
|
||||||
"affinity": "INTEGER",
|
"affinity": "INTEGER",
|
||||||
"notNull": false
|
"notNull": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachmentPreviewUrl",
|
||||||
|
"columnName": "attachmentPreviewUrl",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldPath": "attachmentUrl",
|
"fieldPath": "attachmentUrl",
|
||||||
"columnName": "attachmentUrl",
|
"columnName": "attachmentUrl",
|
||||||
|
@ -176,7 +188,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, '425a0bc96c8aae9d01985b0f4d7579dc')"
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'fd7d1e0ac6ac7d68eb79ffe928dae67a')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -55,6 +55,7 @@ data class Notification(
|
||||||
@ColumnInfo(name = "attachmentType") val attachmentType: String?, // MIME type
|
@ColumnInfo(name = "attachmentType") val attachmentType: String?, // MIME type
|
||||||
@ColumnInfo(name = "attachmentSize") val attachmentSize: Long?, // Size in bytes
|
@ColumnInfo(name = "attachmentSize") val attachmentSize: Long?, // Size in bytes
|
||||||
@ColumnInfo(name = "attachmentExpires") val attachmentExpires: Long?, // Unix timestamp
|
@ColumnInfo(name = "attachmentExpires") val attachmentExpires: Long?, // Unix timestamp
|
||||||
|
@ColumnInfo(name = "attachmentPreviewUrl") val attachmentPreviewUrl: String?,
|
||||||
@ColumnInfo(name = "attachmentUrl") val attachmentUrl: String?,
|
@ColumnInfo(name = "attachmentUrl") val attachmentUrl: String?,
|
||||||
@ColumnInfo(name = "deleted") val deleted: Boolean,
|
@ColumnInfo(name = "deleted") val deleted: Boolean,
|
||||||
)
|
)
|
||||||
|
|
|
@ -123,7 +123,8 @@ class ApiService {
|
||||||
attachmentName = message.attachment?.name,
|
attachmentName = message.attachment?.name,
|
||||||
attachmentType = message.attachment?.type,
|
attachmentType = message.attachment?.type,
|
||||||
attachmentSize = message.attachment?.size,
|
attachmentSize = message.attachment?.size,
|
||||||
attachmentExpires = message.attachment?.expires?.toLong(),
|
attachmentExpires = message.attachment?.expires,
|
||||||
|
attachmentPreviewUrl = message.attachment?.preview_url,
|
||||||
attachmentUrl = message.attachment?.url,
|
attachmentUrl = message.attachment?.url,
|
||||||
notificationId = Random.nextInt(),
|
notificationId = Random.nextInt(),
|
||||||
deleted = false
|
deleted = false
|
||||||
|
@ -158,6 +159,7 @@ class ApiService {
|
||||||
attachmentType = message.attachment?.type,
|
attachmentType = message.attachment?.type,
|
||||||
attachmentSize = message.attachment?.size,
|
attachmentSize = message.attachment?.size,
|
||||||
attachmentExpires = message.attachment?.expires,
|
attachmentExpires = message.attachment?.expires,
|
||||||
|
attachmentPreviewUrl = message.attachment?.preview_url,
|
||||||
attachmentUrl = message.attachment?.url,
|
attachmentUrl = message.attachment?.url,
|
||||||
notificationId = 0,
|
notificationId = 0,
|
||||||
deleted = false
|
deleted = false
|
||||||
|
@ -182,9 +184,10 @@ class ApiService {
|
||||||
@Keep
|
@Keep
|
||||||
private data class Attachment(
|
private data class Attachment(
|
||||||
val name: String,
|
val name: String,
|
||||||
val type: String,
|
val type: String?,
|
||||||
val size: Long,
|
val size: Long?,
|
||||||
val expires: Long,
|
val expires: Long?,
|
||||||
|
val preview_url: String?,
|
||||||
val url: String,
|
val url: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,9 @@ import android.content.Intent
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.media.RingtoneManager
|
import android.media.RingtoneManager
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
@ -22,8 +24,10 @@ import io.heckel.ntfy.util.formatMessage
|
||||||
import io.heckel.ntfy.util.formatTitle
|
import io.heckel.ntfy.util.formatTitle
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
|
import java.io.File
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
|
||||||
class NotificationService(val context: Context) {
|
class NotificationService(val context: Context) {
|
||||||
private val client = OkHttpClient.Builder()
|
private val client = OkHttpClient.Builder()
|
||||||
.callTimeout(15, TimeUnit.SECONDS) // Total timeout for entire request
|
.callTimeout(15, TimeUnit.SECONDS) // Total timeout for entire request
|
||||||
|
@ -35,11 +39,9 @@ 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")
|
||||||
|
|
||||||
val imageAttachment = notification.attachmentUrl != null && (notification.attachmentType?.startsWith("image/") ?: false)
|
|
||||||
if (imageAttachment) {
|
|
||||||
downloadImageAndDisplay(subscription, notification)
|
|
||||||
} else {
|
|
||||||
displayInternal(subscription, notification)
|
displayInternal(subscription, notification)
|
||||||
|
if (notification.attachmentPreviewUrl != null) {
|
||||||
|
downloadPreviewAndUpdate(subscription, notification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,8 +83,19 @@ class NotificationService(val context: Context) {
|
||||||
.setSound(defaultSoundUri)
|
.setSound(defaultSoundUri)
|
||||||
.setContentIntent(pendingIntent) // Click target for notification
|
.setContentIntent(pendingIntent) // Click target for notification
|
||||||
.setAutoCancel(true) // Cancel when notification is clicked
|
.setAutoCancel(true) // Cancel when notification is clicked
|
||||||
|
if (notification.attachmentUrl != null) {
|
||||||
|
val viewIntent = PendingIntent.getActivity(context, 0, Intent(Intent.ACTION_VIEW, Uri.parse(notification.attachmentUrl)), 0)
|
||||||
|
notificationBuilder
|
||||||
|
.addAction(NotificationCompat.Action.Builder(0, "Open", viewIntent).build())
|
||||||
|
.addAction(NotificationCompat.Action.Builder(0, "Copy URL", viewIntent).build())
|
||||||
|
.addAction(NotificationCompat.Action.Builder(0, "Download", pendingIntent).build())
|
||||||
|
}
|
||||||
notificationBuilder = if (bitmap != null) {
|
notificationBuilder = if (bitmap != null) {
|
||||||
notificationBuilder.setStyle(NotificationCompat.BigPictureStyle().bigPicture(bitmap))
|
notificationBuilder
|
||||||
|
.setLargeIcon(bitmap)
|
||||||
|
.setStyle(NotificationCompat.BigPictureStyle()
|
||||||
|
.bigPicture(bitmap)
|
||||||
|
.bigLargeIcon(null))
|
||||||
} else {
|
} else {
|
||||||
notificationBuilder.setStyle(NotificationCompat.BigTextStyle().bigText(message))
|
notificationBuilder.setStyle(NotificationCompat.BigTextStyle().bigText(message))
|
||||||
}
|
}
|
||||||
|
@ -92,9 +105,29 @@ class NotificationService(val context: Context) {
|
||||||
notificationManager.notify(notification.notificationId, notificationBuilder.build())
|
notificationManager.notify(notification.notificationId, notificationBuilder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadImageAndDisplay(subscription: Subscription, notification: Notification) {
|
private fun downloadPreviewAndUpdate(subscription: Subscription, notification: Notification) {
|
||||||
|
val previewUrl = notification.attachmentPreviewUrl ?: return
|
||||||
|
Log.d(TAG, "Downloading preview image $previewUrl")
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(previewUrl)
|
||||||
|
.addHeader("User-Agent", ApiService.USER_AGENT)
|
||||||
|
.build()
|
||||||
|
client.newCall(request).execute().use { response ->
|
||||||
|
if (!response.isSuccessful || response.body == null) {
|
||||||
|
Log.d(TAG, "Preview response failed: ${response.code}")
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Successful response, streaming preview")
|
||||||
|
val bitmap = BitmapFactory.decodeStream(response.body!!.byteStream())
|
||||||
|
displayInternal(subscription, notification, bitmap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun downloadPreviewAndUpdateXXX(subscription: Subscription, notification: Notification) {
|
||||||
val url = notification.attachmentUrl ?: return
|
val url = notification.attachmentUrl ?: return
|
||||||
Log.d(TAG, "Downloading image $url")
|
Log.d(TAG, "Downloading attachment from $url")
|
||||||
|
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
|
@ -102,13 +135,21 @@ class NotificationService(val context: Context) {
|
||||||
.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) {
|
||||||
displayInternal(subscription, notification)
|
Log.d(TAG, "Attachment download failed: ${response.code}")
|
||||||
return
|
} else {
|
||||||
}
|
Log.d(TAG, "Successful response")
|
||||||
|
/*val filename = notification.id
|
||||||
|
val dir = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS + "/ntfy/" + notification.id)
|
||||||
|
context.openFileOutput(filename, Context.MODE_PRIVATE).use {
|
||||||
|
response.body!!.byteStream()
|
||||||
|
}*/
|
||||||
|
// TODO work manager
|
||||||
|
|
||||||
val bitmap = BitmapFactory.decodeStream(response.body!!.byteStream())
|
val bitmap = BitmapFactory.decodeStream(response.body!!.byteStream())
|
||||||
displayInternal(subscription, notification, bitmap)
|
displayInternal(subscription, notification, bitmap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun maybeCreateNotificationChannel(notificationManager: NotificationManager, priority: Int) {
|
private fun maybeCreateNotificationChannel(notificationManager: NotificationManager, priority: Int) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
|
|
@ -60,6 +60,7 @@ class FirebaseService : FirebaseMessagingService() {
|
||||||
val attachmentType = data["attachment_type"]
|
val attachmentType = data["attachment_type"]
|
||||||
val attachmentSize = data["attachment_size"]?.toLongOrNull()
|
val attachmentSize = data["attachment_size"]?.toLongOrNull()
|
||||||
val attachmentExpires = data["attachment_expires"]?.toLongOrNull()
|
val attachmentExpires = data["attachment_expires"]?.toLongOrNull()
|
||||||
|
val attachmentPreviewUrl = data["attachment_preview_url"]
|
||||||
val attachmentUrl = data["attachment_url"]
|
val attachmentUrl = data["attachment_url"]
|
||||||
if (id == null || topic == null || message == null || timestamp == null) {
|
if (id == null || topic == null || message == null || timestamp == null) {
|
||||||
Log.d(TAG, "Discarding unexpected message: from=${remoteMessage.from}, data=${data}")
|
Log.d(TAG, "Discarding unexpected message: from=${remoteMessage.from}, data=${data}")
|
||||||
|
@ -84,6 +85,7 @@ class FirebaseService : FirebaseMessagingService() {
|
||||||
attachmentType = attachmentType,
|
attachmentType = attachmentType,
|
||||||
attachmentSize = attachmentSize,
|
attachmentSize = attachmentSize,
|
||||||
attachmentExpires = attachmentExpires,
|
attachmentExpires = attachmentExpires,
|
||||||
|
attachmentPreviewUrl = attachmentPreviewUrl,
|
||||||
attachmentUrl = attachmentUrl,
|
attachmentUrl = attachmentUrl,
|
||||||
notificationId = Random.nextInt(),
|
notificationId = Random.nextInt(),
|
||||||
deleted = false
|
deleted = false
|
||||||
|
|
Loading…
Reference in a new issue