WIP: Subscription settings
This commit is contained in:
parent
c9643a2173
commit
9f6dd91088
14 changed files with 596 additions and 49 deletions
314
app/schemas/io.heckel.ntfy.db.Database/11.json
Normal file
314
app/schemas/io.heckel.ntfy.db.Database/11.json
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 11,
|
||||||
|
"identityHash": "9a26b356f0d51f2c63fd3e4570b7e645",
|
||||||
|
"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, `minPriority` INTEGER NOT NULL, `autoDelete` 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": "minPriority",
|
||||||
|
"columnName": "minPriority",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "autoDelete",
|
||||||
|
"columnName": "autoDelete",
|
||||||
|
"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, '9a26b356f0d51f2c63fd3e4570b7e645')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import com.google.gson.Gson
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import io.heckel.ntfy.app.Application
|
import io.heckel.ntfy.app.Application
|
||||||
|
import io.heckel.ntfy.db.Repository
|
||||||
import io.heckel.ntfy.util.Log
|
import io.heckel.ntfy.util.Log
|
||||||
import io.heckel.ntfy.util.topicUrl
|
import io.heckel.ntfy.util.topicUrl
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
|
@ -94,6 +95,8 @@ class Backuper(val context: Context) {
|
||||||
topic = s.topic,
|
topic = s.topic,
|
||||||
instant = s.instant,
|
instant = s.instant,
|
||||||
mutedUntil = s.mutedUntil,
|
mutedUntil = s.mutedUntil,
|
||||||
|
minPriority = s.minPriority ?: Repository.MIN_PRIORITY_USE_GLOBAL,
|
||||||
|
autoDelete = s.autoDelete ?: Repository.AUTO_DELETE_USE_GLOBAL,
|
||||||
upAppId = s.upAppId,
|
upAppId = s.upAppId,
|
||||||
upConnectorToken = s.upConnectorToken
|
upConnectorToken = s.upConnectorToken
|
||||||
))
|
))
|
||||||
|
@ -214,6 +217,8 @@ class Backuper(val context: Context) {
|
||||||
topic = s.topic,
|
topic = s.topic,
|
||||||
instant = s.instant,
|
instant = s.instant,
|
||||||
mutedUntil = s.mutedUntil,
|
mutedUntil = s.mutedUntil,
|
||||||
|
minPriority = s.minPriority,
|
||||||
|
autoDelete = s.autoDelete,
|
||||||
upAppId = s.upAppId,
|
upAppId = s.upAppId,
|
||||||
upConnectorToken = s.upConnectorToken
|
upConnectorToken = s.upConnectorToken
|
||||||
)
|
)
|
||||||
|
@ -317,6 +322,8 @@ data class Subscription(
|
||||||
val topic: String,
|
val topic: String,
|
||||||
val instant: Boolean,
|
val instant: Boolean,
|
||||||
val mutedUntil: Long,
|
val mutedUntil: Long,
|
||||||
|
val minPriority: Int?,
|
||||||
|
val autoDelete: Long?,
|
||||||
val upAppId: String?,
|
val upAppId: String?,
|
||||||
val upConnectorToken: String?
|
val upConnectorToken: String?
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,17 +15,18 @@ data class Subscription(
|
||||||
@ColumnInfo(name = "baseUrl") val baseUrl: String,
|
@ColumnInfo(name = "baseUrl") val baseUrl: String,
|
||||||
@ColumnInfo(name = "topic") val topic: String,
|
@ColumnInfo(name = "topic") val topic: String,
|
||||||
@ColumnInfo(name = "instant") val instant: Boolean,
|
@ColumnInfo(name = "instant") val instant: Boolean,
|
||||||
@ColumnInfo(name = "mutedUntil") val mutedUntil: Long, // TODO notificationSound, notificationSchedule
|
@ColumnInfo(name = "mutedUntil") val mutedUntil: Long,
|
||||||
|
@ColumnInfo(name = "minPriority") val minPriority: Int,
|
||||||
|
@ColumnInfo(name = "autoDelete") val autoDelete: Long, // Seconds
|
||||||
@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
|
||||||
@Ignore val state: ConnectionState = ConnectionState.NOT_APPLICABLE
|
@Ignore val state: ConnectionState = ConnectionState.NOT_APPLICABLE
|
||||||
) {
|
) {
|
||||||
constructor(id: Long, baseUrl: String, topic: String, instant: Boolean, mutedUntil: Long, upAppId: String, upConnectorToken: String) :
|
constructor(id: Long, baseUrl: String, topic: String, instant: Boolean, mutedUntil: Long, minPriority: Int, autoDelete: Long, upAppId: String, upConnectorToken: String) :
|
||||||
this(id, baseUrl, topic, instant, mutedUntil, upAppId, upConnectorToken, 0, 0, 0, ConnectionState.NOT_APPLICABLE)
|
this(id, baseUrl, topic, instant, mutedUntil, minPriority, autoDelete, upAppId, upConnectorToken, 0, 0, 0, ConnectionState.NOT_APPLICABLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ConnectionState {
|
enum class ConnectionState {
|
||||||
|
@ -38,6 +39,8 @@ data class SubscriptionWithMetadata(
|
||||||
val topic: String,
|
val topic: String,
|
||||||
val instant: Boolean,
|
val instant: Boolean,
|
||||||
val mutedUntil: Long,
|
val mutedUntil: Long,
|
||||||
|
val autoDelete: Long,
|
||||||
|
val minPriority: Int,
|
||||||
val upAppId: String?,
|
val upAppId: String?,
|
||||||
val upConnectorToken: String?,
|
val upConnectorToken: String?,
|
||||||
val totalCount: Int,
|
val totalCount: Int,
|
||||||
|
@ -139,7 +142,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 = 10)
|
@androidx.room.Database(entities = [Subscription::class, Notification::class, User::class, LogEntry::class], version = 11)
|
||||||
@TypeConverters(Converters::class)
|
@TypeConverters(Converters::class)
|
||||||
abstract class Database : RoomDatabase() {
|
abstract class Database : RoomDatabase() {
|
||||||
abstract fun subscriptionDao(): SubscriptionDao
|
abstract fun subscriptionDao(): SubscriptionDao
|
||||||
|
@ -164,6 +167,7 @@ abstract class Database : RoomDatabase() {
|
||||||
.addMigrations(MIGRATION_7_8)
|
.addMigrations(MIGRATION_7_8)
|
||||||
.addMigrations(MIGRATION_8_9)
|
.addMigrations(MIGRATION_8_9)
|
||||||
.addMigrations(MIGRATION_9_10)
|
.addMigrations(MIGRATION_9_10)
|
||||||
|
.addMigrations(MIGRATION_10_11)
|
||||||
.fallbackToDestructiveMigration()
|
.fallbackToDestructiveMigration()
|
||||||
.build()
|
.build()
|
||||||
this.instance = instance
|
this.instance = instance
|
||||||
|
@ -245,6 +249,13 @@ abstract class Database : RoomDatabase() {
|
||||||
db.execSQL("ALTER TABLE Notification ADD COLUMN actions TEXT")
|
db.execSQL("ALTER TABLE Notification ADD COLUMN actions TEXT")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val MIGRATION_10_11 = object : Migration(10, 11) {
|
||||||
|
override fun migrate(db: SupportSQLiteDatabase) {
|
||||||
|
db.execSQL("ALTER TABLE Notification ADD COLUMN minPriority INT NOT NULL DEFAULT (0)") // 0 = use global setting
|
||||||
|
db.execSQL("ALTER TABLE Notification ADD COLUMN autoDelete INT NOT NULL DEFAULT (0)")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +263,7 @@ abstract class Database : RoomDatabase() {
|
||||||
interface SubscriptionDao {
|
interface SubscriptionDao {
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT
|
SELECT
|
||||||
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.upAppId, s.upConnectorToken,
|
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.upAppId, s.upConnectorToken,
|
||||||
COUNT(n.id) totalCount,
|
COUNT(n.id) totalCount,
|
||||||
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
||||||
IFNULL(MAX(n.timestamp),0) AS lastActive
|
IFNULL(MAX(n.timestamp),0) AS lastActive
|
||||||
|
@ -265,7 +276,7 @@ interface SubscriptionDao {
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT
|
SELECT
|
||||||
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.upAppId, s.upConnectorToken,
|
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.upAppId, s.upConnectorToken,
|
||||||
COUNT(n.id) totalCount,
|
COUNT(n.id) totalCount,
|
||||||
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
||||||
IFNULL(MAX(n.timestamp),0) AS lastActive
|
IFNULL(MAX(n.timestamp),0) AS lastActive
|
||||||
|
@ -278,7 +289,7 @@ interface SubscriptionDao {
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT
|
SELECT
|
||||||
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.upAppId, s.upConnectorToken,
|
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.upAppId, s.upConnectorToken,
|
||||||
COUNT(n.id) totalCount,
|
COUNT(n.id) totalCount,
|
||||||
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
||||||
IFNULL(MAX(n.timestamp),0) AS lastActive
|
IFNULL(MAX(n.timestamp),0) AS lastActive
|
||||||
|
@ -291,7 +302,7 @@ interface SubscriptionDao {
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT
|
SELECT
|
||||||
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.upAppId, s.upConnectorToken,
|
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.upAppId, s.upConnectorToken,
|
||||||
COUNT(n.id) totalCount,
|
COUNT(n.id) totalCount,
|
||||||
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
||||||
IFNULL(MAX(n.timestamp),0) AS lastActive
|
IFNULL(MAX(n.timestamp),0) AS lastActive
|
||||||
|
@ -304,7 +315,7 @@ interface SubscriptionDao {
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT
|
SELECT
|
||||||
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.upAppId, s.upConnectorToken,
|
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.upAppId, s.upConnectorToken,
|
||||||
COUNT(n.id) totalCount,
|
COUNT(n.id) totalCount,
|
||||||
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
||||||
IFNULL(MAX(n.timestamp),0) AS lastActive
|
IFNULL(MAX(n.timestamp),0) AS lastActive
|
||||||
|
@ -357,14 +368,14 @@ interface NotificationDao {
|
||||||
@Query("UPDATE notification SET deleted = 1 WHERE subscriptionId = :subscriptionId")
|
@Query("UPDATE notification SET deleted = 1 WHERE subscriptionId = :subscriptionId")
|
||||||
fun markAllAsDeleted(subscriptionId: Long)
|
fun markAllAsDeleted(subscriptionId: Long)
|
||||||
|
|
||||||
@Query("UPDATE notification SET deleted = 1 WHERE timestamp < :olderThanTimestamp")
|
@Query("UPDATE notification SET deleted = 1 WHERE subscriptionId = :subscriptionId AND timestamp < :olderThanTimestamp")
|
||||||
fun markAsDeletedIfOlderThan(olderThanTimestamp: Long)
|
fun markAsDeletedIfOlderThan(subscriptionId: Long, olderThanTimestamp: Long)
|
||||||
|
|
||||||
@Query("UPDATE notification SET deleted = 0 WHERE id = :notificationId")
|
@Query("UPDATE notification SET deleted = 0 WHERE id = :notificationId")
|
||||||
fun undelete(notificationId: String)
|
fun undelete(notificationId: String)
|
||||||
|
|
||||||
@Query("DELETE FROM notification WHERE timestamp < :olderThanTimestamp")
|
@Query("DELETE FROM notification WHERE subscriptionId = :subscriptionId AND timestamp < :olderThanTimestamp")
|
||||||
fun removeIfOlderThan(olderThanTimestamp: Long)
|
fun removeIfOlderThan(subscriptionId: Long, olderThanTimestamp: Long)
|
||||||
|
|
||||||
@Query("DELETE FROM notification WHERE subscriptionId = :subscriptionId")
|
@Query("DELETE FROM notification WHERE subscriptionId = :subscriptionId")
|
||||||
fun removeAll(subscriptionId: Long)
|
fun removeAll(subscriptionId: Long)
|
||||||
|
|
|
@ -136,12 +136,12 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
|
||||||
notificationDao.markAllAsDeleted(subscriptionId)
|
notificationDao.markAllAsDeleted(subscriptionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun markAsDeletedIfOlderThan(olderThanTimestamp: Long) {
|
fun markAsDeletedIfOlderThan(subscriptionId: Long, olderThanTimestamp: Long) {
|
||||||
notificationDao.markAsDeletedIfOlderThan(olderThanTimestamp)
|
notificationDao.markAsDeletedIfOlderThan(subscriptionId, olderThanTimestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeNotificationsIfOlderThan(olderThanTimestamp: Long) {
|
fun removeNotificationsIfOlderThan(subscriptionId: Long, olderThanTimestamp: Long) {
|
||||||
notificationDao.removeIfOlderThan(olderThanTimestamp)
|
notificationDao.removeIfOlderThan(subscriptionId, olderThanTimestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeAllNotifications(subscriptionId: Long) {
|
fun removeAllNotifications(subscriptionId: Long) {
|
||||||
|
@ -203,7 +203,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setMinPriority(minPriority: Int) {
|
fun setMinPriority(minPriority: Int) {
|
||||||
if (minPriority <= 1) {
|
if (minPriority <= MIN_PRIORITY_ANY) {
|
||||||
sharedPrefs.edit()
|
sharedPrefs.edit()
|
||||||
.remove(SHARED_PREFS_MIN_PRIORITY)
|
.remove(SHARED_PREFS_MIN_PRIORITY)
|
||||||
.apply()
|
.apply()
|
||||||
|
@ -215,7 +215,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMinPriority(): Int {
|
fun getMinPriority(): Int {
|
||||||
return sharedPrefs.getInt(SHARED_PREFS_MIN_PRIORITY, 1) // 1/low means all priorities
|
return sharedPrefs.getInt(SHARED_PREFS_MIN_PRIORITY, MIN_PRIORITY_ANY)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAutoDownloadMaxSize(): Long {
|
fun getAutoDownloadMaxSize(): Long {
|
||||||
|
@ -377,6 +377,8 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
|
||||||
topic = s.topic,
|
topic = s.topic,
|
||||||
instant = s.instant,
|
instant = s.instant,
|
||||||
mutedUntil = s.mutedUntil,
|
mutedUntil = s.mutedUntil,
|
||||||
|
minPriority = s.minPriority,
|
||||||
|
autoDelete = s.autoDelete,
|
||||||
upAppId = s.upAppId,
|
upAppId = s.upAppId,
|
||||||
upConnectorToken = s.upConnectorToken,
|
upConnectorToken = s.upConnectorToken,
|
||||||
totalCount = s.totalCount,
|
totalCount = s.totalCount,
|
||||||
|
@ -397,6 +399,8 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
|
||||||
topic = s.topic,
|
topic = s.topic,
|
||||||
instant = s.instant,
|
instant = s.instant,
|
||||||
mutedUntil = s.mutedUntil,
|
mutedUntil = s.mutedUntil,
|
||||||
|
minPriority = s.minPriority,
|
||||||
|
autoDelete = s.autoDelete,
|
||||||
upAppId = s.upAppId,
|
upAppId = s.upAppId,
|
||||||
upConnectorToken = s.upConnectorToken,
|
upConnectorToken = s.upConnectorToken,
|
||||||
totalCount = s.totalCount,
|
totalCount = s.totalCount,
|
||||||
|
@ -449,6 +453,9 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
|
||||||
|
|
||||||
private const val LAST_TOPICS_COUNT = 3
|
private const val LAST_TOPICS_COUNT = 3
|
||||||
|
|
||||||
|
const val MIN_PRIORITY_USE_GLOBAL = 0
|
||||||
|
const val MIN_PRIORITY_ANY = 1
|
||||||
|
|
||||||
const val MUTED_UNTIL_SHOW_ALL = 0L
|
const val MUTED_UNTIL_SHOW_ALL = 0L
|
||||||
const val MUTED_UNTIL_FOREVER = 1L
|
const val MUTED_UNTIL_FOREVER = 1L
|
||||||
const val MUTED_UNTIL_TOMORROW = 2L
|
const val MUTED_UNTIL_TOMORROW = 2L
|
||||||
|
@ -459,7 +466,8 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
|
||||||
const val AUTO_DOWNLOAD_DEFAULT = ONE_MB
|
const val AUTO_DOWNLOAD_DEFAULT = ONE_MB
|
||||||
|
|
||||||
private const val ONE_DAY_SECONDS = 24 * 60 * 60L
|
private const val ONE_DAY_SECONDS = 24 * 60 * 60L
|
||||||
const val AUTO_DELETE_NEVER = 0L // Values must match values.xml
|
const val AUTO_DELETE_USE_GLOBAL = -1L // Values must match values.xml
|
||||||
|
const val AUTO_DELETE_NEVER = 0L
|
||||||
const val AUTO_DELETE_ONE_DAY_SECONDS = ONE_DAY_SECONDS
|
const val AUTO_DELETE_ONE_DAY_SECONDS = ONE_DAY_SECONDS
|
||||||
const val AUTO_DELETE_THREE_DAYS_SECONDS = 3 * ONE_DAY_SECONDS
|
const val AUTO_DELETE_THREE_DAYS_SECONDS = 3 * ONE_DAY_SECONDS
|
||||||
const val AUTO_DELETE_ONE_WEEK_SECONDS = 7 * ONE_DAY_SECONDS
|
const val AUTO_DELETE_ONE_WEEK_SECONDS = 7 * ONE_DAY_SECONDS
|
||||||
|
|
|
@ -73,7 +73,8 @@ class NotificationDispatcher(val context: Context, val repository: Repository) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
val priority = if (notification.priority > 0) notification.priority else 3
|
val priority = if (notification.priority > 0) notification.priority else 3
|
||||||
if (priority < repository.getMinPriority()) {
|
val minPriority = if (subscription.minPriority > 0) subscription.minPriority else repository.getMinPriority()
|
||||||
|
if (priority < minPriority) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
val detailsVisible = repository.detailViewSubscriptionId.get() == notification.subscriptionId
|
val detailsVisible = repository.detailViewSubscriptionId.get() == notification.subscriptionId
|
||||||
|
|
|
@ -112,6 +112,8 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
|
||||||
topic = topic,
|
topic = topic,
|
||||||
instant = instant,
|
instant = instant,
|
||||||
mutedUntil = 0,
|
mutedUntil = 0,
|
||||||
|
minPriority = Repository.MIN_PRIORITY_USE_GLOBAL,
|
||||||
|
autoDelete = Repository.AUTO_DELETE_USE_GLOBAL,
|
||||||
upAppId = null,
|
upAppId = null,
|
||||||
upConnectorToken = null,
|
upConnectorToken = null,
|
||||||
totalCount = 0,
|
totalCount = 0,
|
||||||
|
@ -348,10 +350,10 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
|
||||||
onClearClick()
|
onClearClick()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
/*R.id.detail_menu_settings -> {
|
R.id.detail_menu_settings -> {
|
||||||
onSettingsClick()
|
onSettingsClick()
|
||||||
true
|
true
|
||||||
}*/
|
}
|
||||||
R.id.detail_menu_unsubscribe -> {
|
R.id.detail_menu_unsubscribe -> {
|
||||||
onDeleteClick()
|
onDeleteClick()
|
||||||
true
|
true
|
||||||
|
@ -549,6 +551,8 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
|
||||||
|
|
||||||
val intent = Intent(this, DetailSettingsActivity::class.java)
|
val intent = Intent(this, DetailSettingsActivity::class.java)
|
||||||
intent.putExtra(EXTRA_SUBSCRIPTION_ID, subscriptionId)
|
intent.putExtra(EXTRA_SUBSCRIPTION_ID, subscriptionId)
|
||||||
|
intent.putExtra(EXTRA_SUBSCRIPTION_BASE_URL, subscriptionBaseUrl)
|
||||||
|
intent.putExtra(EXTRA_SUBSCRIPTION_TOPIC, subscriptionTopic)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -729,5 +733,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "NtfyDetailActivity"
|
const val TAG = "NtfyDetailActivity"
|
||||||
const val EXTRA_SUBSCRIPTION_ID = "subscriptionId"
|
const val EXTRA_SUBSCRIPTION_ID = "subscriptionId"
|
||||||
|
const val EXTRA_SUBSCRIPTION_BASE_URL = "baseUrl"
|
||||||
|
const val EXTRA_SUBSCRIPTION_TOPIC = "topic"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,19 +3,24 @@ package io.heckel.ntfy.ui
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.preference.ListPreference
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceDataStore
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import io.heckel.ntfy.R
|
import io.heckel.ntfy.R
|
||||||
import io.heckel.ntfy.db.Repository
|
import io.heckel.ntfy.db.Repository
|
||||||
import io.heckel.ntfy.db.Subscription
|
import io.heckel.ntfy.db.Subscription
|
||||||
import io.heckel.ntfy.util.Log
|
import io.heckel.ntfy.util.Log
|
||||||
import io.heckel.ntfy.service.SubscriberServiceManager
|
import io.heckel.ntfy.service.SubscriberServiceManager
|
||||||
|
import io.heckel.ntfy.util.formatDateShort
|
||||||
|
import io.heckel.ntfy.util.toPriorityString
|
||||||
|
import io.heckel.ntfy.util.topicShortUrl
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription settings
|
* Subscription settings
|
||||||
*
|
|
||||||
* THIS IS CURRENTLY UNUSED.
|
|
||||||
*/
|
*/
|
||||||
class DetailSettingsActivity : AppCompatActivity() {
|
class DetailSettingsActivity : AppCompatActivity() {
|
||||||
private lateinit var repository: Repository
|
private lateinit var repository: Repository
|
||||||
|
@ -44,7 +49,10 @@ class DetailSettingsActivity : AppCompatActivity() {
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
title = getString(R.string.detail_settings_title)
|
// Title
|
||||||
|
val baseUrl = intent.getStringExtra(DetailActivity.EXTRA_SUBSCRIPTION_BASE_URL) ?: return
|
||||||
|
val topic = intent.getStringExtra(DetailActivity.EXTRA_SUBSCRIPTION_TOPIC) ?: return
|
||||||
|
title = topicShortUrl(baseUrl, topic)
|
||||||
|
|
||||||
// Show 'Back' button
|
// Show 'Back' button
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
@ -58,6 +66,7 @@ class DetailSettingsActivity : AppCompatActivity() {
|
||||||
class SettingsFragment : PreferenceFragmentCompat() {
|
class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
private lateinit var repository: Repository
|
private lateinit var repository: Repository
|
||||||
private lateinit var serviceManager: SubscriberServiceManager
|
private lateinit var serviceManager: SubscriberServiceManager
|
||||||
|
private lateinit var subscription: Subscription
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
setPreferencesFromResource(R.xml.detail_preferences, rootKey)
|
setPreferencesFromResource(R.xml.detail_preferences, rootKey)
|
||||||
|
@ -69,15 +78,130 @@ class DetailSettingsActivity : AppCompatActivity() {
|
||||||
// Load subscription and users
|
// Load subscription and users
|
||||||
val subscriptionId = arguments?.getLong(DetailActivity.EXTRA_SUBSCRIPTION_ID) ?: return
|
val subscriptionId = arguments?.getLong(DetailActivity.EXTRA_SUBSCRIPTION_ID) ?: return
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
val subscription = repository.getSubscription(subscriptionId) ?: return@launch
|
subscription = repository.getSubscription(subscriptionId) ?: return@launch
|
||||||
activity?.runOnUiThread {
|
activity?.runOnUiThread {
|
||||||
loadView(subscription)
|
loadView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadView(subscription: Subscription) {
|
private fun loadView() {
|
||||||
// ...
|
// Notifications muted until
|
||||||
|
val mutedUntilPrefId = context?.getString(R.string.detail_settings_notifications_muted_until_key) ?: return
|
||||||
|
val mutedUntil: ListPreference? = findPreference(mutedUntilPrefId)
|
||||||
|
mutedUntil?.value = subscription.mutedUntil.toString()
|
||||||
|
mutedUntil?.preferenceDataStore = object : PreferenceDataStore() {
|
||||||
|
override fun putString(key: String?, value: String?) {
|
||||||
|
val mutedUntilValue = value?.toLongOrNull() ?:return
|
||||||
|
when (mutedUntilValue) {
|
||||||
|
Repository.MUTED_UNTIL_SHOW_ALL -> save(subscription.copy(mutedUntil = mutedUntilValue))
|
||||||
|
Repository.MUTED_UNTIL_FOREVER -> save(subscription.copy(mutedUntil = mutedUntilValue))
|
||||||
|
Repository.MUTED_UNTIL_TOMORROW -> {
|
||||||
|
val date = Calendar.getInstance()
|
||||||
|
date.add(Calendar.DAY_OF_MONTH, 1)
|
||||||
|
date.set(Calendar.HOUR_OF_DAY, 8)
|
||||||
|
date.set(Calendar.MINUTE, 30)
|
||||||
|
date.set(Calendar.SECOND, 0)
|
||||||
|
date.set(Calendar.MILLISECOND, 0)
|
||||||
|
save(subscription.copy(mutedUntil = date.timeInMillis/1000))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val mutedUntilTimestamp = System.currentTimeMillis()/1000 + mutedUntilValue * 60
|
||||||
|
save(subscription.copy(mutedUntil = mutedUntilTimestamp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun getString(key: String?, defValue: String?): String {
|
||||||
|
return subscription.mutedUntil.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutedUntil?.summaryProvider = Preference.SummaryProvider<ListPreference> { _ ->
|
||||||
|
val mutedUntilValue = subscription.mutedUntil
|
||||||
|
when (mutedUntilValue) {
|
||||||
|
Repository.MUTED_UNTIL_SHOW_ALL -> getString(R.string.settings_notifications_muted_until_show_all)
|
||||||
|
Repository.MUTED_UNTIL_FOREVER -> getString(R.string.settings_notifications_muted_until_forever)
|
||||||
|
else -> {
|
||||||
|
val formattedDate = formatDateShort(mutedUntilValue)
|
||||||
|
getString(R.string.settings_notifications_muted_until_x, formattedDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimum priority
|
||||||
|
val minPriorityPrefId = context?.getString(R.string.detail_settings_notifications_min_priority_key) ?: return
|
||||||
|
val minPriority: ListPreference? = findPreference(minPriorityPrefId)
|
||||||
|
minPriority?.value = subscription.minPriority.toString()
|
||||||
|
minPriority?.preferenceDataStore = object : PreferenceDataStore() {
|
||||||
|
override fun putString(key: String?, value: String?) {
|
||||||
|
val minPriorityValue = value?.toIntOrNull() ?:return
|
||||||
|
save(subscription.copy(minPriority = minPriorityValue))
|
||||||
|
}
|
||||||
|
override fun getString(key: String?, defValue: String?): String {
|
||||||
|
return subscription.minPriority.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
minPriority?.summaryProvider = Preference.SummaryProvider<ListPreference> { pref ->
|
||||||
|
var value = pref.value.toIntOrNull() ?: Repository.MIN_PRIORITY_USE_GLOBAL
|
||||||
|
val global = value == Repository.MIN_PRIORITY_USE_GLOBAL
|
||||||
|
if (value == Repository.MIN_PRIORITY_USE_GLOBAL) {
|
||||||
|
value = repository.getMinPriority()
|
||||||
|
}
|
||||||
|
val summary = when (value) {
|
||||||
|
1 -> getString(R.string.settings_notifications_min_priority_summary_any)
|
||||||
|
5 -> getString(R.string.settings_notifications_min_priority_summary_max)
|
||||||
|
else -> {
|
||||||
|
val minPriorityString = toPriorityString(requireContext(), value)
|
||||||
|
getString(R.string.settings_notifications_min_priority_summary_x_or_higher, value, minPriorityString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maybeAppendGlobal(summary, global)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto delete
|
||||||
|
val autoDeletePrefId = context?.getString(R.string.detail_settings_notifications_auto_delete_key) ?: return
|
||||||
|
val autoDelete: ListPreference? = findPreference(autoDeletePrefId)
|
||||||
|
autoDelete?.value = subscription.autoDelete.toString()
|
||||||
|
autoDelete?.preferenceDataStore = object : PreferenceDataStore() {
|
||||||
|
override fun putString(key: String?, value: String?) {
|
||||||
|
val seconds = value?.toLongOrNull() ?:return
|
||||||
|
save(subscription.copy(autoDelete = seconds))
|
||||||
|
}
|
||||||
|
override fun getString(key: String?, defValue: String?): String {
|
||||||
|
return subscription.autoDelete.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
autoDelete?.summaryProvider = Preference.SummaryProvider<ListPreference> { pref ->
|
||||||
|
var seconds = pref.value.toLongOrNull() ?: Repository.AUTO_DELETE_USE_GLOBAL
|
||||||
|
val global = seconds == Repository.AUTO_DELETE_USE_GLOBAL
|
||||||
|
if (seconds == Repository.AUTO_DELETE_USE_GLOBAL) {
|
||||||
|
seconds = repository.getAutoDeleteSeconds()
|
||||||
|
}
|
||||||
|
val summary = when (seconds) {
|
||||||
|
Repository.AUTO_DELETE_NEVER -> getString(R.string.settings_notifications_auto_delete_summary_never)
|
||||||
|
Repository.AUTO_DELETE_ONE_DAY_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_one_day)
|
||||||
|
Repository.AUTO_DELETE_THREE_DAYS_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_three_days)
|
||||||
|
Repository.AUTO_DELETE_ONE_WEEK_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_one_week)
|
||||||
|
Repository.AUTO_DELETE_ONE_MONTH_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_one_month)
|
||||||
|
Repository.AUTO_DELETE_THREE_MONTHS_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_three_months)
|
||||||
|
else -> getString(R.string.settings_notifications_auto_delete_summary_one_month) // Must match default const
|
||||||
|
}
|
||||||
|
maybeAppendGlobal(summary, global)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun save(newSubscription: Subscription) {
|
||||||
|
subscription = newSubscription
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
repository.updateSubscription(newSubscription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun maybeAppendGlobal(summary: String, global: Boolean): String {
|
||||||
|
return if (global) {
|
||||||
|
summary + " (" + getString(R.string.settings_global_setting_suffix) + ")"
|
||||||
|
} else {
|
||||||
|
summary
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -425,6 +425,8 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
|
||||||
topic = topic,
|
topic = topic,
|
||||||
instant = instant,
|
instant = instant,
|
||||||
mutedUntil = 0,
|
mutedUntil = 0,
|
||||||
|
minPriority = Repository.MIN_PRIORITY_USE_GLOBAL,
|
||||||
|
autoDelete = Repository.AUTO_DELETE_USE_GLOBAL,
|
||||||
upAppId = null,
|
upAppId = null,
|
||||||
upConnectorToken = null,
|
upConnectorToken = null,
|
||||||
totalCount = 0,
|
totalCount = 0,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
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.db.Repository
|
||||||
import io.heckel.ntfy.db.Subscription
|
import io.heckel.ntfy.db.Subscription
|
||||||
import io.heckel.ntfy.service.SubscriberServiceManager
|
import io.heckel.ntfy.service.SubscriberServiceManager
|
||||||
import io.heckel.ntfy.util.Log
|
import io.heckel.ntfy.util.Log
|
||||||
|
@ -74,6 +75,8 @@ class BroadcastReceiver : android.content.BroadcastReceiver() {
|
||||||
topic = topic,
|
topic = topic,
|
||||||
instant = true, // No Firebase, always instant!
|
instant = true, // No Firebase, always instant!
|
||||||
mutedUntil = 0,
|
mutedUntil = 0,
|
||||||
|
minPriority = Repository.MIN_PRIORITY_USE_GLOBAL,
|
||||||
|
autoDelete = Repository.AUTO_DELETE_USE_GLOBAL,
|
||||||
upAppId = appId,
|
upAppId = appId,
|
||||||
upConnectorToken = connectorToken,
|
upConnectorToken = connectorToken,
|
||||||
totalCount = 0,
|
totalCount = 0,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import io.heckel.ntfy.db.ATTACHMENT_PROGRESS_DELETED
|
||||||
import io.heckel.ntfy.db.Repository
|
import io.heckel.ntfy.db.Repository
|
||||||
import io.heckel.ntfy.ui.DetailAdapter
|
import io.heckel.ntfy.ui.DetailAdapter
|
||||||
import io.heckel.ntfy.util.Log
|
import io.heckel.ntfy.util.Log
|
||||||
|
import io.heckel.ntfy.util.topicShortUrl
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@ -53,29 +54,38 @@ class DeleteWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx
|
||||||
val newNotification = notification.copy(attachment = newAttachment)
|
val newNotification = notification.copy(attachment = newAttachment)
|
||||||
repository.updateNotification(newNotification)
|
repository.updateNotification(newNotification)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(DetailAdapter.TAG, "Failed to delete attachment for notification: ${e.message}", e)
|
Log.w(TAG, "Failed to delete attachment for notification: ${e.message}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteExpiredNotifications() {
|
private suspend fun deleteExpiredNotifications() {
|
||||||
Log.d(TAG, "Deleting expired notifications")
|
Log.d(TAG, "Deleting expired notifications")
|
||||||
val repository = Repository.getInstance(applicationContext)
|
val repository = Repository.getInstance(applicationContext)
|
||||||
val deleteAfterSeconds = repository.getAutoDeleteSeconds()
|
val subscriptions = repository.getSubscriptions()
|
||||||
if (deleteAfterSeconds == Repository.AUTO_DELETE_NEVER) {
|
subscriptions.forEach { subscription ->
|
||||||
Log.d(TAG, "Not deleting any notifications; global setting set to NEVER")
|
val logId = topicShortUrl(subscription.baseUrl, subscription.topic)
|
||||||
return
|
val deleteAfterSeconds = if (subscription.autoDelete == Repository.AUTO_DELETE_USE_GLOBAL) {
|
||||||
|
repository.getAutoDeleteSeconds()
|
||||||
|
} else {
|
||||||
|
subscription.autoDelete
|
||||||
|
}
|
||||||
|
if (deleteAfterSeconds == Repository.AUTO_DELETE_NEVER) {
|
||||||
|
Log.d(TAG, "[$logId] Not deleting any notifications; global setting set to NEVER")
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark as deleted
|
||||||
|
val markDeletedOlderThanTimestamp = (System.currentTimeMillis()/1000) - deleteAfterSeconds
|
||||||
|
Log.d(TAG, "[$logId] Marking notifications older than $markDeletedOlderThanTimestamp as deleted")
|
||||||
|
repository.markAsDeletedIfOlderThan(subscription.id, markDeletedOlderThanTimestamp)
|
||||||
|
|
||||||
|
// Hard delete
|
||||||
|
val deleteOlderThanTimestamp = (System.currentTimeMillis()/1000) - HARD_DELETE_AFTER_SECONDS
|
||||||
|
Log.d(TAG, "[$logId] Hard deleting notifications older than $markDeletedOlderThanTimestamp")
|
||||||
|
repository.removeNotificationsIfOlderThan(subscription.id, deleteOlderThanTimestamp)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark as deleted
|
|
||||||
val markDeletedOlderThanTimestamp = (System.currentTimeMillis()/1000) - deleteAfterSeconds
|
|
||||||
Log.d(TAG, "Marking notifications older than $markDeletedOlderThanTimestamp as deleted")
|
|
||||||
repository.markAsDeletedIfOlderThan(markDeletedOlderThanTimestamp)
|
|
||||||
|
|
||||||
// Hard delete
|
|
||||||
val deleteOlderThanTimestamp = (System.currentTimeMillis()/1000) - HARD_DELETE_AFTER_SECONDS
|
|
||||||
Log.d(TAG, "Hard deleting notifications older than $markDeletedOlderThanTimestamp")
|
|
||||||
repository.removeNotificationsIfOlderThan(deleteOlderThanTimestamp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
app:showAsAction="ifRoom" android:icon="@drawable/ic_bolt_outline_white_24dp"/>
|
app:showAsAction="ifRoom" android:icon="@drawable/ic_bolt_outline_white_24dp"/>
|
||||||
<item android:id="@+id/detail_menu_disable_instant" android:title="@string/detail_menu_disable_instant"
|
<item android:id="@+id/detail_menu_disable_instant" android:title="@string/detail_menu_disable_instant"
|
||||||
android:icon="@drawable/ic_bolt_white_24dp" app:showAsAction="ifRoom"/>
|
android:icon="@drawable/ic_bolt_white_24dp" app:showAsAction="ifRoom"/>
|
||||||
<item android:id="@+id/detail_menu_test" android:title="@string/detail_menu_test"/>
|
<item android:id="@+id/detail_menu_settings" android:title="@string/detail_menu_settings"/>
|
||||||
<item android:id="@+id/detail_menu_copy_url" android:title="@string/detail_menu_copy_url"/>
|
<item android:id="@+id/detail_menu_copy_url" android:title="@string/detail_menu_copy_url"/>
|
||||||
<item android:id="@+id/detail_menu_clear" android:title="@string/detail_menu_clear"/>
|
<item android:id="@+id/detail_menu_clear" android:title="@string/detail_menu_clear"/>
|
||||||
<!--<item android:id="@+id/detail_menu_settings" android:title="@string/detail_menu_settings"/>-->
|
<item android:id="@+id/detail_menu_test" android:title="@string/detail_menu_test"/>
|
||||||
<item android:id="@+id/detail_menu_unsubscribe" android:title="@string/detail_menu_unsubscribe"/>
|
<item android:id="@+id/detail_menu_unsubscribe" android:title="@string/detail_menu_unsubscribe"/>
|
||||||
</menu>
|
</menu>
|
||||||
|
|
|
@ -334,6 +334,8 @@
|
||||||
<string name="settings_about_version_title">Version</string>
|
<string name="settings_about_version_title">Version</string>
|
||||||
<string name="settings_about_version_format">ntfy %1$s (%2$s)</string>
|
<string name="settings_about_version_format">ntfy %1$s (%2$s)</string>
|
||||||
<string name="settings_about_version_copied_to_clipboard_message">Copied to clipboard</string>
|
<string name="settings_about_version_copied_to_clipboard_message">Copied to clipboard</string>
|
||||||
|
<string name="settings_global_setting_title">Use global setting</string>
|
||||||
|
<string name="settings_global_setting_suffix">global</string>
|
||||||
|
|
||||||
<!-- User add/edit dialog -->
|
<!-- User add/edit dialog -->
|
||||||
<string name="user_dialog_title_add">Add user</string>
|
<string name="user_dialog_title_add">Add user</string>
|
||||||
|
|
|
@ -30,6 +30,11 @@
|
||||||
<string name="settings_advanced_connection_protocol_key" translatable="false">ConnectionProtocol</string>
|
<string name="settings_advanced_connection_protocol_key" translatable="false">ConnectionProtocol</string>
|
||||||
<string name="settings_about_version_key" translatable="false">Version</string>
|
<string name="settings_about_version_key" translatable="false">Version</string>
|
||||||
|
|
||||||
|
<!-- Detail settings constants -->
|
||||||
|
<string name="detail_settings_notifications_muted_until_key" translatable="false">SubscriptionMutedUntil</string>
|
||||||
|
<string name="detail_settings_notifications_min_priority_key" translatable="false">SubscriptionMinPriority</string>
|
||||||
|
<string name="detail_settings_notifications_auto_delete_key" translatable="false">SubscriptionAutoDelete</string>
|
||||||
|
|
||||||
<!-- Main settings -->
|
<!-- Main settings -->
|
||||||
<string-array name="settings_notifications_muted_until_entries">
|
<string-array name="settings_notifications_muted_until_entries">
|
||||||
<item>@string/notification_dialog_show_all</item>
|
<item>@string/notification_dialog_show_all</item>
|
||||||
|
@ -63,6 +68,22 @@
|
||||||
<item>4</item>
|
<item>4</item>
|
||||||
<item>5</item>
|
<item>5</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string-array name="detail_settings_notifications_min_priority_entries">
|
||||||
|
<item>@string/settings_global_setting_title</item>
|
||||||
|
<item>@string/settings_notifications_min_priority_min</item>
|
||||||
|
<item>@string/settings_notifications_min_priority_low</item>
|
||||||
|
<item>@string/settings_notifications_min_priority_default</item>
|
||||||
|
<item>@string/settings_notifications_min_priority_high</item>
|
||||||
|
<item>@string/settings_notifications_min_priority_max</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="detail_settings_notifications_min_priority_values">
|
||||||
|
<item>0</item> <!-- Same as Repository.MIN_PRIORITY_USE_GLOBAL -->
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
<item>3</item>
|
||||||
|
<item>4</item>
|
||||||
|
<item>5</item>
|
||||||
|
</string-array>
|
||||||
<string-array name="settings_notifications_auto_download_entries">
|
<string-array name="settings_notifications_auto_download_entries">
|
||||||
<item>@string/settings_notifications_auto_download_never</item>
|
<item>@string/settings_notifications_auto_download_never</item>
|
||||||
<item>@string/settings_notifications_auto_download_always</item>
|
<item>@string/settings_notifications_auto_download_always</item>
|
||||||
|
@ -99,6 +120,24 @@
|
||||||
<item>2592000</item>
|
<item>2592000</item>
|
||||||
<item>7776000</item>
|
<item>7776000</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string-array name="detail_settings_notifications_auto_delete_entries">
|
||||||
|
<item>@string/settings_global_setting_title</item>
|
||||||
|
<item>@string/settings_notifications_auto_delete_never</item>
|
||||||
|
<item>@string/settings_notifications_auto_delete_one_day</item>
|
||||||
|
<item>@string/settings_notifications_auto_delete_three_days</item>
|
||||||
|
<item>@string/settings_notifications_auto_delete_one_week</item>
|
||||||
|
<item>@string/settings_notifications_auto_delete_one_month</item>
|
||||||
|
<item>@string/settings_notifications_auto_delete_three_months</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="detail_settings_notifications_auto_delete_values">
|
||||||
|
<item>-1</item> <!-- Same as Repository.AUTO_DELETE_USE_GLOBAL -->
|
||||||
|
<item>0</item>
|
||||||
|
<item>86400</item>
|
||||||
|
<item>259200</item>
|
||||||
|
<item>604800</item>
|
||||||
|
<item>2592000</item>
|
||||||
|
<item>7776000</item>
|
||||||
|
</string-array>
|
||||||
<string-array name="settings_advanced_connection_protocol_entries">
|
<string-array name="settings_advanced_connection_protocol_entries">
|
||||||
<item>@string/settings_advanced_connection_protocol_entry_jsonhttp</item>
|
<item>@string/settings_advanced_connection_protocol_entry_jsonhttp</item>
|
||||||
<item>@string/settings_advanced_connection_protocol_entry_ws</item>
|
<item>@string/settings_advanced_connection_protocol_entry_ws</item>
|
||||||
|
|
|
@ -1,3 +1,23 @@
|
||||||
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
|
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
app:title="@string/detail_settings_title">
|
app:title="@string/detail_settings_title">
|
||||||
|
<PreferenceCategory app:title="@string/settings_notifications_header">
|
||||||
|
<ListPreference
|
||||||
|
app:key="@string/detail_settings_notifications_muted_until_key"
|
||||||
|
app:title="@string/settings_notifications_muted_until_title"
|
||||||
|
app:entries="@array/settings_notifications_muted_until_entries"
|
||||||
|
app:entryValues="@array/settings_notifications_muted_until_values"
|
||||||
|
app:defaultValue="0"/>
|
||||||
|
<ListPreference
|
||||||
|
app:key="@string/detail_settings_notifications_min_priority_key"
|
||||||
|
app:title="@string/settings_notifications_min_priority_title"
|
||||||
|
app:entries="@array/detail_settings_notifications_min_priority_entries"
|
||||||
|
app:entryValues="@array/detail_settings_notifications_min_priority_values"
|
||||||
|
app:defaultValue="0"/> <!-- Same as Repository.MIN_PRIORITY_USE_GLOBAL -->
|
||||||
|
<ListPreference
|
||||||
|
app:key="@string/detail_settings_notifications_auto_delete_key"
|
||||||
|
app:title="@string/settings_notifications_auto_delete_title"
|
||||||
|
app:entries="@array/detail_settings_notifications_auto_delete_entries"
|
||||||
|
app:entryValues="@array/detail_settings_notifications_auto_delete_values"
|
||||||
|
app:defaultValue="-1"/> <!-- Same as Repository.AUTO_DELETE_USE_GLOBAL -->
|
||||||
|
</PreferenceCategory>
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
Loading…
Reference in a new issue