Remove preview; progress in notification
This commit is contained in:
parent
d440d0a633
commit
dced0a2e06
10 changed files with 74 additions and 90 deletions
|
@ -2,7 +2,7 @@
|
||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 6,
|
"version": 6,
|
||||||
"identityHash": "1ab02dd84a7f2655b4fc651574b24240",
|
"identityHash": "fc725df9153ee7088ae8024428b7f2cf",
|
||||||
"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, `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`))",
|
"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_url` TEXT, `attachment_contentUri` TEXT, `attachment_progress` INTEGER, PRIMARY KEY(`id`, `subscriptionId`))",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "id",
|
"fieldPath": "id",
|
||||||
|
@ -167,12 +167,6 @@
|
||||||
"affinity": "INTEGER",
|
"affinity": "INTEGER",
|
||||||
"notNull": false
|
"notNull": false
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldPath": "attachment.previewUrl",
|
|
||||||
"columnName": "attachment_previewUrl",
|
|
||||||
"affinity": "TEXT",
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldPath": "attachment.url",
|
"fieldPath": "attachment.url",
|
||||||
"columnName": "attachment_url",
|
"columnName": "attachment_url",
|
||||||
|
@ -185,12 +179,6 @@
|
||||||
"affinity": "TEXT",
|
"affinity": "TEXT",
|
||||||
"notNull": false
|
"notNull": false
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldPath": "attachment.previewFile",
|
|
||||||
"columnName": "attachment_previewFile",
|
|
||||||
"affinity": "TEXT",
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldPath": "attachment.progress",
|
"fieldPath": "attachment.progress",
|
||||||
"columnName": "attachment_progress",
|
"columnName": "attachment_progress",
|
||||||
|
@ -212,7 +200,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, '1ab02dd84a7f2655b4fc651574b24240')"
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'fc725df9153ee7088ae8024428b7f2cf')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -108,5 +108,6 @@
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||||
android:resource="@drawable/ic_notification"/>
|
android:resource="@drawable/ic_notification"/>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -63,14 +63,12 @@ data class Attachment(
|
||||||
@ColumnInfo(name = "type") val type: String?, // MIME type
|
@ColumnInfo(name = "type") val type: String?, // MIME type
|
||||||
@ColumnInfo(name = "size") val size: Long?, // Size in bytes
|
@ColumnInfo(name = "size") val size: Long?, // Size in bytes
|
||||||
@ColumnInfo(name = "expires") val expires: Long?, // Unix timestamp
|
@ColumnInfo(name = "expires") val expires: Long?, // Unix timestamp
|
||||||
@ColumnInfo(name = "previewUrl") val previewUrl: String?,
|
|
||||||
@ColumnInfo(name = "url") val url: String,
|
@ColumnInfo(name = "url") val url: String,
|
||||||
@ColumnInfo(name = "contentUri") val contentUri: String?,
|
@ColumnInfo(name = "contentUri") val contentUri: String?,
|
||||||
@ColumnInfo(name = "previewFile") val previewFile: String?,
|
|
||||||
@ColumnInfo(name = "progress") val progress: Int,
|
@ColumnInfo(name = "progress") val progress: Int,
|
||||||
) {
|
) {
|
||||||
constructor(name: String?, type: String?, size: Long?, expires: Long?, previewUrl: String?, url: String) :
|
constructor(name: String?, type: String?, size: Long?, expires: Long?, url: String) :
|
||||||
this(name, type, size, expires, previewUrl, url, null, null, 0)
|
this(name, type, size, expires, url, null, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@androidx.room.Database(entities = [Subscription::class, Notification::class], version = 6)
|
@androidx.room.Database(entities = [Subscription::class, Notification::class], version = 6)
|
||||||
|
|
|
@ -7,11 +7,7 @@ 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.Attachment
|
||||||
import io.heckel.ntfy.data.Notification
|
import io.heckel.ntfy.data.Notification
|
||||||
import io.heckel.ntfy.util.topicUrl
|
import io.heckel.ntfy.util.*
|
||||||
import io.heckel.ntfy.util.topicUrlJson
|
|
||||||
import io.heckel.ntfy.util.topicUrlJsonPoll
|
|
||||||
import io.heckel.ntfy.util.toPriority
|
|
||||||
import io.heckel.ntfy.util.joinTags
|
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -119,7 +115,6 @@ class ApiService {
|
||||||
type = message.attachment.type,
|
type = message.attachment.type,
|
||||||
size = message.attachment.size,
|
size = message.attachment.size,
|
||||||
expires = message.attachment.expires,
|
expires = message.attachment.expires,
|
||||||
previewUrl = message.attachment.preview_url,
|
|
||||||
url = message.attachment.url,
|
url = message.attachment.url,
|
||||||
)
|
)
|
||||||
} else null
|
} else null
|
||||||
|
@ -160,7 +155,6 @@ class ApiService {
|
||||||
type = message.attachment.type,
|
type = message.attachment.type,
|
||||||
size = message.attachment.size,
|
size = message.attachment.size,
|
||||||
expires = message.attachment.expires,
|
expires = message.attachment.expires,
|
||||||
previewUrl = message.attachment.preview_url,
|
|
||||||
url = message.attachment.url,
|
url = message.attachment.url,
|
||||||
)
|
)
|
||||||
} else null
|
} else null
|
||||||
|
@ -174,7 +168,7 @@ class ApiService {
|
||||||
tags = joinTags(message.tags),
|
tags = joinTags(message.tags),
|
||||||
click = message.click ?: "",
|
click = message.click ?: "",
|
||||||
attachment = attachment,
|
attachment = attachment,
|
||||||
notificationId = 0, // zero!
|
notificationId = 0, // zero: when we poll, we do not want a notificationId!
|
||||||
deleted = false
|
deleted = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -192,16 +186,15 @@ class ApiService {
|
||||||
val click: String?,
|
val click: String?,
|
||||||
val title: String?,
|
val title: String?,
|
||||||
val message: String,
|
val message: String,
|
||||||
val attachment: Attachment?,
|
val attachment: MessageAttachment?,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
private data class Attachment(
|
private data class MessageAttachment(
|
||||||
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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package io.heckel.ntfy.msg
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
@ -13,10 +12,9 @@ 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
|
||||||
|
import io.heckel.ntfy.msg.NotificationService.Companion.PROGRESS_DONE
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import java.io.File
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class AttachmentDownloadWorker(private val context: Context, params: WorkerParameters) : Worker(context, params) {
|
class AttachmentDownloadWorker(private val context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||||
|
@ -36,35 +34,10 @@ class AttachmentDownloadWorker(private val context: Context, params: WorkerParam
|
||||||
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()
|
||||||
val attachment = notification.attachment ?: return Result.failure()
|
val attachment = notification.attachment ?: return Result.failure()
|
||||||
if (attachment.previewUrl != null) {
|
|
||||||
downloadPreview(subscription, notification, attachment)
|
|
||||||
}
|
|
||||||
downloadAttachment(repository, subscription, notification, attachment)
|
downloadAttachment(repository, subscription, notification, attachment)
|
||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadPreview(subscription: Subscription, notification: Notification, attachment: Attachment) {
|
|
||||||
val url = attachment.previewUrl ?: return
|
|
||||||
Log.d(TAG, "Downloading preview from $url")
|
|
||||||
|
|
||||||
val request = Request.Builder()
|
|
||||||
.url(url)
|
|
||||||
.addHeader("User-Agent", ApiService.USER_AGENT)
|
|
||||||
.build()
|
|
||||||
client.newCall(request).execute().use { response ->
|
|
||||||
if (!response.isSuccessful || response.body == null) {
|
|
||||||
throw Exception("Preview download failed: ${response.code}")
|
|
||||||
}
|
|
||||||
val previewFile = File(applicationContext.cacheDir.absolutePath, "preview-" + notification.id)
|
|
||||||
Log.d(TAG, "Downloading preview to cache file: $previewFile")
|
|
||||||
FileOutputStream(previewFile).use { fileOut ->
|
|
||||||
response.body!!.byteStream().copyTo(fileOut)
|
|
||||||
}
|
|
||||||
Log.d(TAG, "Preview downloaded; updating notification")
|
|
||||||
notifier.update(subscription, notification)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun downloadAttachment(repository: Repository, subscription: Subscription, notification: Notification, attachment: Attachment) {
|
private fun downloadAttachment(repository: Repository, subscription: Subscription, notification: Notification, attachment: Attachment) {
|
||||||
Log.d(TAG, "Downloading attachment from ${attachment.url}")
|
Log.d(TAG, "Downloading attachment from ${attachment.url}")
|
||||||
|
|
||||||
|
@ -111,7 +84,7 @@ class AttachmentDownloadWorker(private val context: Context, params: WorkerParam
|
||||||
val newAttachment = attachment.copy(contentUri = uri.toString())
|
val newAttachment = attachment.copy(contentUri = uri.toString())
|
||||||
val newNotification = notification.copy(attachment = newAttachment)
|
val newNotification = notification.copy(attachment = newAttachment)
|
||||||
repository.updateNotification(newNotification)
|
repository.updateNotification(newNotification)
|
||||||
notifier.update(subscription, newNotification)
|
notifier.update(subscription, newNotification, progress = PROGRESS_DONE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ class NotificationDispatcher(val context: Context, val repository: Repository) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (download) {
|
if (download) {
|
||||||
// Download attachment (+ preview if available) in the background via WorkManager
|
// Download attachment in the background via WorkManager
|
||||||
// The indirection via WorkManager is required since this code may be executed
|
// 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.
|
// in a doze state and Internet may not be available. It's also best practice apparently.
|
||||||
scheduleAttachmentDownload(notification)
|
scheduleAttachmentDownload(notification)
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
package io.heckel.ntfy.msg
|
package io.heckel.ntfy.msg
|
||||||
|
|
||||||
import android.app.NotificationChannel
|
import android.app.*
|
||||||
import android.app.NotificationManager
|
import android.content.*
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.app.TaskStackBuilder
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
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.net.Uri
|
||||||
|
@ -14,18 +9,15 @@ import android.os.Build
|
||||||
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
|
||||||
import androidx.work.OneTimeWorkRequest
|
|
||||||
import androidx.work.WorkManager
|
|
||||||
import androidx.work.workDataOf
|
|
||||||
import io.heckel.ntfy.R
|
import io.heckel.ntfy.R
|
||||||
import io.heckel.ntfy.data.Notification
|
import io.heckel.ntfy.data.Notification
|
||||||
import io.heckel.ntfy.data.Repository
|
|
||||||
import io.heckel.ntfy.data.Subscription
|
import io.heckel.ntfy.data.Subscription
|
||||||
import io.heckel.ntfy.ui.DetailActivity
|
import io.heckel.ntfy.ui.DetailActivity
|
||||||
import io.heckel.ntfy.ui.MainActivity
|
import io.heckel.ntfy.ui.MainActivity
|
||||||
|
import io.heckel.ntfy.util.formatBytes
|
||||||
|
import io.heckel.ntfy.util.formatDateShort
|
||||||
import io.heckel.ntfy.util.formatMessage
|
import io.heckel.ntfy.util.formatMessage
|
||||||
import io.heckel.ntfy.util.formatTitle
|
import io.heckel.ntfy.util.formatTitle
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class NotificationService(val context: Context) {
|
class NotificationService(val context: Context) {
|
||||||
private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
@ -54,7 +46,7 @@ class NotificationService(val context: Context) {
|
||||||
|
|
||||||
private fun displayInternal(subscription: Subscription, notification: Notification, update: Boolean = false, progress: Int = PROGRESS_NONE) {
|
private fun displayInternal(subscription: Subscription, notification: Notification, update: Boolean = false, progress: Int = PROGRESS_NONE) {
|
||||||
val title = formatTitle(subscription, notification)
|
val title = formatTitle(subscription, notification)
|
||||||
val message = formatMessage(notification)
|
val message = maybeWithAttachmentInfo(formatMessage(notification), notification, progress)
|
||||||
val channelId = toChannelId(notification.priority)
|
val channelId = toChannelId(notification.priority)
|
||||||
val builder = NotificationCompat.Builder(context, channelId)
|
val builder = NotificationCompat.Builder(context, channelId)
|
||||||
.setSmallIcon(R.drawable.ic_notification)
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
|
@ -68,12 +60,25 @@ class NotificationService(val context: Context) {
|
||||||
maybeSetSound(builder, update)
|
maybeSetSound(builder, update)
|
||||||
maybeSetProgress(builder, progress)
|
maybeSetProgress(builder, progress)
|
||||||
maybeAddOpenAction(builder, notification)
|
maybeAddOpenAction(builder, notification)
|
||||||
maybeAddCopyUrlAction(builder, notification)
|
maybeAddBrowseAction(builder, notification)
|
||||||
|
|
||||||
maybeCreateNotificationChannel(notification.priority)
|
maybeCreateNotificationChannel(notification.priority)
|
||||||
notificationManager.notify(notification.notificationId, builder.build())
|
notificationManager.notify(notification.notificationId, builder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun maybeWithAttachmentInfo(message: String, notification: Notification, progress: Int): String {
|
||||||
|
if (progress < 0 || notification.attachment == null) return message
|
||||||
|
val att = notification.attachment
|
||||||
|
val infos = mutableListOf<String>()
|
||||||
|
if (att.name != null) infos.add(att.name)
|
||||||
|
if (att.size != null) infos.add(formatBytes(att.size))
|
||||||
|
//if (att.expires != null && att.expires != 0L) infos.add(formatDateShort(att.expires))
|
||||||
|
if (progress >= 0) infos.add("${progress}%")
|
||||||
|
if (infos.size == 0) return message
|
||||||
|
if (progress < 100) return "Downloading ${infos.joinToString(", ")}\n${message}"
|
||||||
|
return "${message}\nFile: ${infos.joinToString(", ")}"
|
||||||
|
}
|
||||||
|
|
||||||
private fun maybeSetSound(builder: NotificationCompat.Builder, update: Boolean) {
|
private fun maybeSetSound(builder: NotificationCompat.Builder, update: Boolean) {
|
||||||
if (!update) {
|
if (!update) {
|
||||||
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
|
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
|
||||||
|
@ -84,10 +89,13 @@ class NotificationService(val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setStyle(builder: NotificationCompat.Builder, notification: Notification, message: String) {
|
private fun setStyle(builder: NotificationCompat.Builder, notification: Notification, message: String) {
|
||||||
val previewFile = File(context.applicationContext.cacheDir.absolutePath, "preview-" + notification.id)
|
val contentUri = notification.attachment?.contentUri
|
||||||
if (previewFile.exists()) {
|
val isImage = listOf("image/jpeg", "image/png").contains(notification.attachment?.type)
|
||||||
|
if (contentUri != null && isImage) {
|
||||||
try {
|
try {
|
||||||
val bitmap = BitmapFactory.decodeFile(previewFile.absolutePath)
|
val resolver = context.applicationContext.contentResolver
|
||||||
|
val bitmapStream = resolver.openInputStream(Uri.parse(contentUri))
|
||||||
|
val bitmap = BitmapFactory.decodeStream(bitmapStream)
|
||||||
builder
|
builder
|
||||||
.setLargeIcon(bitmap)
|
.setLargeIcon(bitmap)
|
||||||
.setStyle(NotificationCompat.BigPictureStyle()
|
.setStyle(NotificationCompat.BigPictureStyle()
|
||||||
|
@ -116,26 +124,27 @@ class NotificationService(val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun maybeSetProgress(builder: NotificationCompat.Builder, progress: Int) {
|
private fun maybeSetProgress(builder: NotificationCompat.Builder, progress: Int) {
|
||||||
if (progress >= 0) {
|
if (progress in 0..99) {
|
||||||
builder.setProgress(100, progress, false)
|
builder.setProgress(100, progress, false)
|
||||||
} else {
|
} else {
|
||||||
builder.setProgress(0, 0, false) // Remove progress bar
|
builder.setProgress(0, 0, false) // Remove progress bar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun maybeAddOpenAction(notificationBuilder: NotificationCompat.Builder, notification: Notification) {
|
private fun maybeAddOpenAction(builder: NotificationCompat.Builder, notification: Notification) {
|
||||||
if (notification.attachment?.contentUri != null) {
|
if (notification.attachment?.contentUri != null) {
|
||||||
val contentUri = Uri.parse(notification.attachment.contentUri)
|
val contentUri = Uri.parse(notification.attachment.contentUri)
|
||||||
val openIntent = PendingIntent.getActivity(context, 0, Intent(Intent.ACTION_VIEW, contentUri), 0)
|
val intent = Intent(Intent.ACTION_VIEW, contentUri)
|
||||||
notificationBuilder.addAction(NotificationCompat.Action.Builder(0, "Open", openIntent).build())
|
val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
|
||||||
|
builder.addAction(NotificationCompat.Action.Builder(0, context.getString(R.string.notification_popup_action_open), pendingIntent).build())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun maybeAddCopyUrlAction(builder: NotificationCompat.Builder, notification: Notification) {
|
private fun maybeAddBrowseAction(builder: NotificationCompat.Builder, notification: Notification) {
|
||||||
if (notification.attachment?.url != null) {
|
if (notification.attachment?.contentUri != null) {
|
||||||
// XXXXXXXXx
|
val intent = Intent(DownloadManager.ACTION_VIEW_DOWNLOADS)
|
||||||
val copyUrlIntent = PendingIntent.getActivity(context, 0, Intent(Intent.ACTION_VIEW, Uri.parse(notification.attachment.url)), 0)
|
val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
|
||||||
builder.addAction(NotificationCompat.Action.Builder(0, "Copy URL", copyUrlIntent).build())
|
builder.addAction(NotificationCompat.Action.Builder(0, context.getString(R.string.notification_popup_action_browse), pendingIntent).build())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,8 +211,10 @@ class NotificationService(val context: Context) {
|
||||||
companion object {
|
companion object {
|
||||||
const val PROGRESS_NONE = -1
|
const val PROGRESS_NONE = -1
|
||||||
const val PROGRESS_INDETERMINATE = -2
|
const val PROGRESS_INDETERMINATE = -2
|
||||||
|
const val PROGRESS_DONE = 100
|
||||||
|
|
||||||
private const val TAG = "NtfyNotifService"
|
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"
|
||||||
|
|
|
@ -7,6 +7,7 @@ import io.heckel.ntfy.data.Notification
|
||||||
import io.heckel.ntfy.data.Subscription
|
import io.heckel.ntfy.data.Subscription
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
|
import java.text.StringCharacterIterator
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
fun topicUrl(baseUrl: String, topic: String) = "${baseUrl}/${topic}"
|
fun topicUrl(baseUrl: String, topic: String) = "${baseUrl}/${topic}"
|
||||||
|
@ -19,8 +20,8 @@ fun topicShortUrl(baseUrl: String, topic: String) =
|
||||||
.replace("https://", "")
|
.replace("https://", "")
|
||||||
|
|
||||||
fun formatDateShort(timestampSecs: Long): String {
|
fun formatDateShort(timestampSecs: Long): String {
|
||||||
val mutedUntilDate = Date(timestampSecs*1000)
|
val date = Date(timestampSecs*1000)
|
||||||
return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(mutedUntilDate)
|
return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(date)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toPriority(priority: Int?): Int {
|
fun toPriority(priority: Int?): Int {
|
||||||
|
@ -126,3 +127,20 @@ fun randomString(len: Int): String {
|
||||||
inline fun <T1: Any, T2: Any, R: Any> safeLet(p1: T1?, p2: T2?, block: (T1, T2)->R?): R? {
|
inline fun <T1: Any, T2: Any, R: Any> safeLet(p1: T1?, p2: T2?, block: (T1, T2)->R?): R? {
|
||||||
return if (p1 != null && p2 != null) block(p1, p2) else null
|
return if (p1 != null && p2 != null) block(p1, p2) else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun formatBytes(bytes: Long): String {
|
||||||
|
val absB = if (bytes == Long.MIN_VALUE) Long.MAX_VALUE else Math.abs(bytes)
|
||||||
|
if (absB < 1024) {
|
||||||
|
return "$bytes B"
|
||||||
|
}
|
||||||
|
var value = absB
|
||||||
|
val ci = StringCharacterIterator("KMGTPE")
|
||||||
|
var i = 40
|
||||||
|
while (i >= 0 && absB > 0xfffccccccccccccL shr i) {
|
||||||
|
value = value shr 10
|
||||||
|
ci.next()
|
||||||
|
i -= 10
|
||||||
|
}
|
||||||
|
value *= java.lang.Long.signum(bytes).toLong()
|
||||||
|
return java.lang.String.format("%.1f %ciB", value / 1024.0, ci.current())
|
||||||
|
}
|
||||||
|
|
|
@ -144,6 +144,10 @@
|
||||||
<string name="notification_dialog_tomorrow">Until tomorrow</string>
|
<string name="notification_dialog_tomorrow">Until tomorrow</string>
|
||||||
<string name="notification_dialog_forever">Forever</string>
|
<string name="notification_dialog_forever">Forever</string>
|
||||||
|
|
||||||
|
<!-- Notification popup -->
|
||||||
|
<string name="notification_popup_action_open">Open</string>
|
||||||
|
<string name="notification_popup_action_browse">Browse</string>
|
||||||
|
|
||||||
<!-- Settings -->
|
<!-- Settings -->
|
||||||
<string name="settings_title">Settings</string>
|
<string name="settings_title">Settings</string>
|
||||||
<string name="settings_notifications_header">Notifications</string>
|
<string name="settings_notifications_header">Notifications</string>
|
||||||
|
|
|
@ -62,7 +62,6 @@ 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"]
|
||||||
val truncated = (data["truncated"] ?: "") == "1"
|
val truncated = (data["truncated"] ?: "") == "1"
|
||||||
if (id == null || topic == null || message == null || timestamp == null) {
|
if (id == null || topic == null || message == null || timestamp == null) {
|
||||||
|
@ -88,7 +87,6 @@ class FirebaseService : FirebaseMessagingService() {
|
||||||
type = attachmentType,
|
type = attachmentType,
|
||||||
size = attachmentSize,
|
size = attachmentSize,
|
||||||
expires = attachmentExpires,
|
expires = attachmentExpires,
|
||||||
previewUrl = attachmentPreviewUrl,
|
|
||||||
url = attachmentUrl,
|
url = attachmentUrl,
|
||||||
)
|
)
|
||||||
} else null
|
} else null
|
||||||
|
|
Loading…
Reference in a new issue