WIP
This commit is contained in:
parent
f62b7fa952
commit
686616d4d2
8 changed files with 357 additions and 72 deletions
302
app/schemas/io.heckel.ntfy.db.Database/10.json
Normal file
302
app/schemas/io.heckel.ntfy.db.Database/10.json
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 10,
|
||||||
|
"identityHash": "c1b4f54d1d3111dc5c8f02e8fa960ceb",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "Subscription",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `baseUrl` TEXT NOT NULL, `topic` TEXT NOT NULL, `instant` INTEGER NOT NULL, `mutedUntil` INTEGER NOT NULL, `upAppId` TEXT, `upConnectorToken` TEXT, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "baseUrl",
|
||||||
|
"columnName": "baseUrl",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "topic",
|
||||||
|
"columnName": "topic",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "instant",
|
||||||
|
"columnName": "instant",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "mutedUntil",
|
||||||
|
"columnName": "mutedUntil",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "upAppId",
|
||||||
|
"columnName": "upAppId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "upConnectorToken",
|
||||||
|
"columnName": "upConnectorToken",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_Subscription_baseUrl_topic",
|
||||||
|
"unique": true,
|
||||||
|
"columnNames": [
|
||||||
|
"baseUrl",
|
||||||
|
"topic"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Subscription_baseUrl_topic` ON `${TABLE_NAME}` (`baseUrl`, `topic`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_Subscription_upConnectorToken",
|
||||||
|
"unique": true,
|
||||||
|
"columnNames": [
|
||||||
|
"upConnectorToken"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Subscription_upConnectorToken` ON `${TABLE_NAME}` (`upConnectorToken`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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, `actions` TEXT, `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": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "subscriptionId",
|
||||||
|
"columnName": "subscriptionId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timestamp",
|
||||||
|
"columnName": "timestamp",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "message",
|
||||||
|
"columnName": "message",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "encoding",
|
||||||
|
"columnName": "encoding",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationId",
|
||||||
|
"columnName": "notificationId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "priority",
|
||||||
|
"columnName": "priority",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tags",
|
||||||
|
"columnName": "tags",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "click",
|
||||||
|
"columnName": "click",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "actions",
|
||||||
|
"columnName": "actions",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "deleted",
|
||||||
|
"columnName": "deleted",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"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.url",
|
||||||
|
"columnName": "attachment_url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachment.contentUri",
|
||||||
|
"columnName": "attachment_contentUri",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachment.progress",
|
||||||
|
"columnName": "attachment_progress",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id",
|
||||||
|
"subscriptionId"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "User",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`baseUrl` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, PRIMARY KEY(`baseUrl`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "baseUrl",
|
||||||
|
"columnName": "baseUrl",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "username",
|
||||||
|
"columnName": "username",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "password",
|
||||||
|
"columnName": "password",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"baseUrl"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "Log",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `tag` TEXT NOT NULL, `level` INTEGER NOT NULL, `message` TEXT NOT NULL, `exception` TEXT)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timestamp",
|
||||||
|
"columnName": "timestamp",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tag",
|
||||||
|
"columnName": "tag",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "level",
|
||||||
|
"columnName": "level",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "message",
|
||||||
|
"columnName": "message",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "exception",
|
||||||
|
"columnName": "exception",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"views": [],
|
||||||
|
"setupQueries": [
|
||||||
|
"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, 'c1b4f54d1d3111dc5c8f02e8fa960ceb')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 9,
|
"version": 9,
|
||||||
"identityHash": "c1b4f54d1d3111dc5c8f02e8fa960ceb",
|
"identityHash": "5bab75c3b41c53c9855fe3a7ef8f0669",
|
||||||
"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, `actions` TEXT, `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, `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",
|
||||||
|
@ -145,12 +145,6 @@
|
||||||
"affinity": "TEXT",
|
"affinity": "TEXT",
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldPath": "actions",
|
|
||||||
"columnName": "actions",
|
|
||||||
"affinity": "TEXT",
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldPath": "deleted",
|
"fieldPath": "deleted",
|
||||||
"columnName": "deleted",
|
"columnName": "deleted",
|
||||||
|
@ -296,7 +290,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, 'c1b4f54d1d3111dc5c8f02e8fa960ceb')"
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5bab75c3b41c53c9855fe3a7ef8f0669')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -78,9 +78,13 @@ data class Attachment(
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
data class Action(
|
data class Action(
|
||||||
|
@ColumnInfo(name = "id") val id: String, // Synthetic ID to identify result, and easily pass via Broadcast and WorkManager
|
||||||
@ColumnInfo(name = "action") val action: String,
|
@ColumnInfo(name = "action") val action: String,
|
||||||
@ColumnInfo(name = "label") val label: String,
|
@ColumnInfo(name = "label") val label: String,
|
||||||
@ColumnInfo(name = "url") val url: String?,
|
@ColumnInfo(name = "url") val url: String?, // used in "view" and "http"
|
||||||
|
@ColumnInfo(name = "method") val method: String?, // used in "http"
|
||||||
|
@ColumnInfo(name = "headers") val headers: Map<String,String>?, // used in "http"
|
||||||
|
@ColumnInfo(name = "body") val body: String?, // used in "http"
|
||||||
)
|
)
|
||||||
|
|
||||||
class Converters {
|
class Converters {
|
||||||
|
@ -126,7 +130,7 @@ data class LogEntry(
|
||||||
this(0, timestamp, tag, level, message, exception)
|
this(0, timestamp, tag, level, message, exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
@androidx.room.Database(entities = [Subscription::class, Notification::class, User::class, LogEntry::class], version = 9)
|
@androidx.room.Database(entities = [Subscription::class, Notification::class, User::class, LogEntry::class], version = 10)
|
||||||
@TypeConverters(Converters::class)
|
@TypeConverters(Converters::class)
|
||||||
abstract class Database : RoomDatabase() {
|
abstract class Database : RoomDatabase() {
|
||||||
abstract fun subscriptionDao(): SubscriptionDao
|
abstract fun subscriptionDao(): SubscriptionDao
|
||||||
|
|
|
@ -32,9 +32,13 @@ data class MessageAttachment(
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
data class MessageAction(
|
data class MessageAction(
|
||||||
|
val id: String,
|
||||||
val action: String,
|
val action: String,
|
||||||
val label: String,
|
val label: String,
|
||||||
val url: String?,
|
val url: String?, // used in "view" and "http"
|
||||||
|
val method: String?, // used in "http"
|
||||||
|
val headers: Map<String,String>?, // used in "http"
|
||||||
|
val body: String?, // used in "http"
|
||||||
)
|
)
|
||||||
|
|
||||||
const val MESSAGE_ENCODING_BASE64 = "base64"
|
const val MESSAGE_ENCODING_BASE64 = "base64"
|
||||||
|
|
|
@ -6,6 +6,7 @@ import io.heckel.ntfy.db.Action
|
||||||
import io.heckel.ntfy.db.Attachment
|
import io.heckel.ntfy.db.Attachment
|
||||||
import io.heckel.ntfy.db.Notification
|
import io.heckel.ntfy.db.Notification
|
||||||
import io.heckel.ntfy.util.joinTags
|
import io.heckel.ntfy.util.joinTags
|
||||||
|
import io.heckel.ntfy.util.randomString
|
||||||
import io.heckel.ntfy.util.toPriority
|
import io.heckel.ntfy.util.toPriority
|
||||||
|
|
||||||
class NotificationParser {
|
class NotificationParser {
|
||||||
|
@ -31,8 +32,8 @@ class NotificationParser {
|
||||||
)
|
)
|
||||||
} else null
|
} else null
|
||||||
val actions = if (message.actions != null) {
|
val actions = if (message.actions != null) {
|
||||||
message.actions.map { action ->
|
message.actions.map { a ->
|
||||||
Action(action.action, action.label, action.url)
|
Action(a.id, a.action, a.label, a.url, a.method, a.headers, a.body)
|
||||||
}
|
}
|
||||||
} else null
|
} else null
|
||||||
val notification = Notification(
|
val notification = Notification(
|
||||||
|
|
|
@ -16,6 +16,7 @@ import io.heckel.ntfy.ui.Colors
|
||||||
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.*
|
import io.heckel.ntfy.util.*
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
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
|
||||||
|
@ -197,9 +198,9 @@ class NotificationService(val context: Context) {
|
||||||
|
|
||||||
private fun maybeAddCustomActions(builder: NotificationCompat.Builder, notification: Notification) {
|
private fun maybeAddCustomActions(builder: NotificationCompat.Builder, notification: Notification) {
|
||||||
notification.actions?.forEach { action ->
|
notification.actions?.forEach { action ->
|
||||||
when (action.action) {
|
when (action.action.lowercase(Locale.getDefault())) {
|
||||||
"view" -> maybeAddViewUserAction(builder, action)
|
ACTION_VIEW -> maybeAddViewUserAction(builder, action)
|
||||||
"http-post" -> maybeAddHttpPostUserAction(builder, notification, action)
|
ACTION_HTTP -> maybeAddHttpUserAction(builder, notification, action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,12 +219,11 @@ class NotificationService(val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun maybeAddHttpPostUserAction(builder: NotificationCompat.Builder, notification: Notification, action: Action) {
|
private fun maybeAddHttpUserAction(builder: NotificationCompat.Builder, notification: Notification, action: Action) {
|
||||||
val intent = Intent(context, UserActionBroadcastReceiver::class.java).apply {
|
val intent = Intent(context, UserActionBroadcastReceiver::class.java).apply {
|
||||||
|
putExtra(BROADCAST_EXTRA_TYPE, BROADCAST_TYPE_USER_ACTION)
|
||||||
putExtra(BROADCAST_EXTRA_NOTIFICATION_ID, notification.id)
|
putExtra(BROADCAST_EXTRA_NOTIFICATION_ID, notification.id)
|
||||||
putExtra(BROADCAST_EXTRA_TYPE, BROADCAST_TYPE_HTTP)
|
putExtra(BROADCAST_EXTRA_ACTION_ID, action.id)
|
||||||
putExtra(BROADCAST_EXTRA_ACTION, action.action)
|
|
||||||
putExtra(BROADCAST_EXTRA_URL, action.url)
|
|
||||||
}
|
}
|
||||||
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||||
builder.addAction(NotificationCompat.Action.Builder(0, action.label, pendingIntent).build())
|
builder.addAction(NotificationCompat.Action.Builder(0, action.label, pendingIntent).build())
|
||||||
|
@ -231,27 +231,16 @@ class NotificationService(val context: Context) {
|
||||||
|
|
||||||
class UserActionBroadcastReceiver : BroadcastReceiver() {
|
class UserActionBroadcastReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
Log.d(TAG, "Received $intent")
|
Log.d(TAG, "Notification user action intent received: $intent")
|
||||||
val type = intent.getStringExtra(BROADCAST_EXTRA_TYPE) ?: return
|
val type = intent.getStringExtra(BROADCAST_EXTRA_TYPE) ?: return
|
||||||
val notificationId = intent.getStringExtra(BROADCAST_EXTRA_NOTIFICATION_ID) ?: return
|
val notificationId = intent.getStringExtra(BROADCAST_EXTRA_NOTIFICATION_ID) ?: return
|
||||||
when (type) {
|
|
||||||
BROADCAST_TYPE_DOWNLOAD_START, BROADCAST_TYPE_DOWNLOAD_CANCEL -> handleDownloadAction(context, type, notificationId)
|
|
||||||
BROADCAST_TYPE_HTTP -> handleCustomUserAction(context, intent, type, notificationId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleDownloadAction(context: Context, type: String, notificationId: String) {
|
|
||||||
when (type) {
|
when (type) {
|
||||||
BROADCAST_TYPE_DOWNLOAD_START -> DownloadManager.enqueue(context, notificationId, userAction = true)
|
BROADCAST_TYPE_DOWNLOAD_START -> DownloadManager.enqueue(context, notificationId, userAction = true)
|
||||||
BROADCAST_TYPE_DOWNLOAD_CANCEL -> DownloadManager.cancel(context, notificationId)
|
BROADCAST_TYPE_DOWNLOAD_CANCEL -> DownloadManager.cancel(context, notificationId)
|
||||||
}
|
BROADCAST_TYPE_USER_ACTION -> {
|
||||||
}
|
val actionId = intent.getStringExtra(BROADCAST_EXTRA_ACTION_ID) ?: return
|
||||||
|
UserActionManager.enqueue(context, notificationId, actionId)
|
||||||
private fun handleCustomUserAction(context: Context, intent: Intent, type: String, notificationId: String) {
|
}
|
||||||
val action = intent.getStringExtra(BROADCAST_EXTRA_ACTION) ?: return
|
|
||||||
val url = intent.getStringExtra(BROADCAST_EXTRA_URL) ?: return
|
|
||||||
when (type) {
|
|
||||||
BROADCAST_TYPE_HTTP -> UserActionManager.enqueue(context, notificationId, action, url)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,12 +310,15 @@ class NotificationService(val context: Context) {
|
||||||
|
|
||||||
private const val BROADCAST_EXTRA_TYPE = "type"
|
private const val BROADCAST_EXTRA_TYPE = "type"
|
||||||
private const val BROADCAST_EXTRA_NOTIFICATION_ID = "notificationId"
|
private const val BROADCAST_EXTRA_NOTIFICATION_ID = "notificationId"
|
||||||
private const val BROADCAST_EXTRA_ACTION = "action"
|
private const val BROADCAST_EXTRA_ACTION_ID = "action"
|
||||||
private const val BROADCAST_EXTRA_URL = "url"
|
private const val BROADCAST_EXTRA_ACTION_JSON = "actionJson"
|
||||||
|
|
||||||
private const val BROADCAST_TYPE_DOWNLOAD_START = "io.heckel.ntfy.DOWNLOAD_ACTION_START"
|
private const val BROADCAST_TYPE_DOWNLOAD_START = "io.heckel.ntfy.DOWNLOAD_ACTION_START"
|
||||||
private const val BROADCAST_TYPE_DOWNLOAD_CANCEL = "io.heckel.ntfy.DOWNLOAD_ACTION_CANCEL"
|
private const val BROADCAST_TYPE_DOWNLOAD_CANCEL = "io.heckel.ntfy.DOWNLOAD_ACTION_CANCEL"
|
||||||
private const val BROADCAST_TYPE_HTTP = "io.heckel.ntfy.USER_ACTION_HTTP"
|
private const val BROADCAST_TYPE_USER_ACTION = "io.heckel.ntfy.USER_ACTION"
|
||||||
|
|
||||||
|
private const val ACTION_VIEW = "view"
|
||||||
|
private const val ACTION_HTTP = "http"
|
||||||
|
|
||||||
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"
|
||||||
|
|
|
@ -6,10 +6,6 @@ import androidx.work.OneTimeWorkRequest
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import androidx.work.workDataOf
|
import androidx.work.workDataOf
|
||||||
import io.heckel.ntfy.util.Log
|
import io.heckel.ntfy.util.Log
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger user actions clicked from notification popups.
|
* Trigger user actions clicked from notification popups.
|
||||||
|
@ -21,15 +17,14 @@ object UserActionManager {
|
||||||
private const val TAG = "NtfyUserActionEx"
|
private const val TAG = "NtfyUserActionEx"
|
||||||
private const val WORK_NAME_PREFIX = "io.heckel.ntfy.USER_ACTION_"
|
private const val WORK_NAME_PREFIX = "io.heckel.ntfy.USER_ACTION_"
|
||||||
|
|
||||||
fun enqueue(context: Context, notificationId: String, action: String, url: String) {
|
fun enqueue(context: Context, notificationId: String, actionId: String) {
|
||||||
val workManager = WorkManager.getInstance(context)
|
val workManager = WorkManager.getInstance(context)
|
||||||
val workName = WORK_NAME_PREFIX + notificationId + action + url
|
val workName = WORK_NAME_PREFIX + notificationId + "_" + actionId
|
||||||
Log.d(TAG,"Enqueuing work to execute user action for notification $notificationId, work: $workName")
|
Log.d(TAG,"Enqueuing work to execute user action for notification $notificationId, action $actionId, work: $workName")
|
||||||
val workRequest = OneTimeWorkRequest.Builder(UserActionWorker::class.java)
|
val workRequest = OneTimeWorkRequest.Builder(UserActionWorker::class.java)
|
||||||
.setInputData(workDataOf(
|
.setInputData(workDataOf(
|
||||||
UserActionWorker.INPUT_DATA_ID to notificationId,
|
UserActionWorker.INPUT_DATA_NOTIFICATION_ID to notificationId,
|
||||||
UserActionWorker.INPUT_DATA_ACTION to action,
|
UserActionWorker.INPUT_DATA_ACTION_ID to actionId,
|
||||||
UserActionWorker.INPUT_DATA_URL to url,
|
|
||||||
))
|
))
|
||||||
.build()
|
.build()
|
||||||
workManager.enqueueUniqueWork(workName, ExistingWorkPolicy.KEEP, workRequest)
|
workManager.enqueueUniqueWork(workName, ExistingWorkPolicy.KEEP, workRequest)
|
||||||
|
|
|
@ -1,25 +1,14 @@
|
||||||
package io.heckel.ntfy.msg
|
package io.heckel.ntfy.msg
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.webkit.MimeTypeMap
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.core.content.FileProvider
|
|
||||||
import androidx.work.Worker
|
import androidx.work.Worker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import io.heckel.ntfy.BuildConfig
|
|
||||||
import io.heckel.ntfy.R
|
|
||||||
import io.heckel.ntfy.app.Application
|
import io.heckel.ntfy.app.Application
|
||||||
import io.heckel.ntfy.db.*
|
import io.heckel.ntfy.db.Action
|
||||||
import io.heckel.ntfy.util.Log
|
import io.heckel.ntfy.util.Log
|
||||||
import io.heckel.ntfy.util.ensureSafeNewFile
|
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import okhttp3.Response
|
|
||||||
import java.io.File
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class UserActionWorker(private val context: Context, params: WorkerParameters) : Worker(context, params) {
|
class UserActionWorker(private val context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||||
|
@ -32,23 +21,28 @@ class UserActionWorker(private val context: Context, params: WorkerParameters) :
|
||||||
|
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
if (context.applicationContext !is Application) return Result.failure()
|
if (context.applicationContext !is Application) return Result.failure()
|
||||||
val notificationId = inputData.getString(INPUT_DATA_ID) ?: return Result.failure()
|
val notificationId = inputData.getString(INPUT_DATA_NOTIFICATION_ID) ?: return Result.failure()
|
||||||
val action = inputData.getString(INPUT_DATA_ACTION) ?: return Result.failure()
|
val actionId = inputData.getString(INPUT_DATA_ACTION_ID) ?: return Result.failure()
|
||||||
val url = inputData.getString(INPUT_DATA_URL) ?: return Result.failure()
|
|
||||||
val app = context.applicationContext as Application
|
val app = context.applicationContext as Application
|
||||||
|
val notification = app.repository.getNotification(notificationId) ?: return Result.failure()
|
||||||
|
val action = notification.actions?.first { it.id == actionId } ?: return Result.failure()
|
||||||
|
|
||||||
http(context, url)
|
Log.d(TAG, "Executing action $action for notification $notification")
|
||||||
|
http(context, action)
|
||||||
|
|
||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun http(context: Context, url: String) { // FIXME Worker!
|
fun http(context: Context, action: Action) { // FIXME Worker!
|
||||||
Log.d(TAG, "HTTP POST againt $url")
|
val url = action.url ?: return
|
||||||
|
val method = action.method ?: "GET"
|
||||||
|
val body = action.body ?: ""
|
||||||
|
Log.d(TAG, "HTTP POST againt ${action.url}")
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
.addHeader("User-Agent", ApiService.USER_AGENT)
|
.addHeader("User-Agent", ApiService.USER_AGENT)
|
||||||
.method("POST", "".toRequestBody())
|
.method(method, body.toRequestBody())
|
||||||
.build()
|
.build()
|
||||||
client.newCall(request).execute().use { response ->
|
client.newCall(request).execute().use { response ->
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
|
@ -59,9 +53,8 @@ class UserActionWorker(private val context: Context, params: WorkerParameters) :
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val INPUT_DATA_ID = "id"
|
const val INPUT_DATA_NOTIFICATION_ID = "notificationId"
|
||||||
const val INPUT_DATA_ACTION = "action"
|
const val INPUT_DATA_ACTION_ID = "actionId"
|
||||||
const val INPUT_DATA_URL = "url"
|
|
||||||
|
|
||||||
private const val TAG = "NtfyUserActWrk"
|
private const val TAG = "NtfyUserActWrk"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue