diff --git a/app/schemas/io.heckel.ntfy.db.Database/12.json b/app/schemas/io.heckel.ntfy.db.Database/12.json index e9e36a1..f5d24cf 100644 --- a/app/schemas/io.heckel.ntfy.db.Database/12.json +++ b/app/schemas/io.heckel.ntfy.db.Database/12.json @@ -2,11 +2,11 @@ "formatVersion": 1, "database": { "version": 12, - "identityHash": "d230005f4d9824ba9aa34c61003bdcbb", + "identityHash": "2be334bfcfa91ab5ffde61a5b295565d", "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, `lastNotificationId` TEXT, `icon` TEXT, `upAppId` TEXT, `upConnectorToken` TEXT, `displayName` TEXT, PRIMARY KEY(`id`))", + "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, `lastNotificationId` TEXT, `icon` TEXT, `upAppId` TEXT, `upConnectorToken` TEXT, `displayName` TEXT, `ownNotificationChannels` INTEGER NOT NULL, PRIMARY KEY(`id`))", "fields": [ { "fieldPath": "id", @@ -79,6 +79,12 @@ "columnName": "displayName", "affinity": "TEXT", "notNull": false + }, + { + "fieldPath": "ownNotificationChannels", + "columnName": "ownNotificationChannels", + "affinity": "INTEGER", + "notNull": true } ], "primaryKey": { @@ -338,7 +344,7 @@ "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, 'd230005f4d9824ba9aa34c61003bdcbb')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2be334bfcfa91ab5ffde61a5b295565d')" ] } } \ No newline at end of file diff --git a/app/schemas/io.heckel.ntfy.db.Database/13.json b/app/schemas/io.heckel.ntfy.db.Database/13.json new file mode 100644 index 0000000..68ded30 --- /dev/null +++ b/app/schemas/io.heckel.ntfy.db.Database/13.json @@ -0,0 +1,350 @@ +{ + "formatVersion": 1, + "database": { + "version": 13, + "identityHash": "2be334bfcfa91ab5ffde61a5b295565d", + "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, `lastNotificationId` TEXT, `icon` TEXT, `upAppId` TEXT, `upConnectorToken` TEXT, `displayName` TEXT, `ownNotificationChannels` INTEGER NOT NULL, 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": "lastNotificationId", + "columnName": "lastNotificationId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "upAppId", + "columnName": "upAppId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "upConnectorToken", + "columnName": "upConnectorToken", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownNotificationChannels", + "columnName": "ownNotificationChannels", + "affinity": "INTEGER", + "notNull": true + } + ], + "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, `icon_url` TEXT, `icon_contentUri` TEXT, `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": "icon.url", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "icon.contentUri", + "columnName": "icon_contentUri", + "affinity": "TEXT", + "notNull": false + }, + { + "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, '2be334bfcfa91ab5ffde61a5b295565d')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/io/heckel/ntfy/backup/Backuper.kt b/app/src/main/java/io/heckel/ntfy/backup/Backuper.kt index 8c87f90..5e80c57 100644 --- a/app/src/main/java/io/heckel/ntfy/backup/Backuper.kt +++ b/app/src/main/java/io/heckel/ntfy/backup/Backuper.kt @@ -99,6 +99,7 @@ class Backuper(val context: Context) { baseUrl = s.baseUrl, topic = s.topic, instant = s.instant, + ownNotificationChannels = s.ownNotificationChannels, mutedUntil = s.mutedUntil, minPriority = s.minPriority ?: Repository.MIN_PRIORITY_USE_GLOBAL, autoDelete = s.autoDelete ?: Repository.AUTO_DELETE_USE_GLOBAL, @@ -236,6 +237,7 @@ class Backuper(val context: Context) { baseUrl = s.baseUrl, topic = s.topic, instant = s.instant, + ownNotificationChannels = s.ownNotificationChannels, mutedUntil = s.mutedUntil, minPriority = s.minPriority, autoDelete = s.autoDelete, @@ -353,6 +355,7 @@ data class Subscription( val baseUrl: String, val topic: String, val instant: Boolean, + val ownNotificationChannels: Boolean, val mutedUntil: Long, val minPriority: Int?, val autoDelete: Long?, diff --git a/app/src/main/java/io/heckel/ntfy/db/Database.kt b/app/src/main/java/io/heckel/ntfy/db/Database.kt index 62daf0a..8b05a5a 100644 --- a/app/src/main/java/io/heckel/ntfy/db/Database.kt +++ b/app/src/main/java/io/heckel/ntfy/db/Database.kt @@ -23,13 +23,14 @@ data class Subscription( @ColumnInfo(name = "upAppId") val upAppId: String?, // UnifiedPush application package name @ColumnInfo(name = "upConnectorToken") val upConnectorToken: String?, // UnifiedPush connector token @ColumnInfo(name = "displayName") val displayName: String?, + @ColumnInfo(name = "ownNotificationChannels") val ownNotificationChannels: Boolean, @Ignore val totalCount: Int = 0, // Total notifications @Ignore val newCount: Int = 0, // New notifications @Ignore val lastActive: Long = 0, // Unix timestamp @Ignore val state: ConnectionState = ConnectionState.NOT_APPLICABLE ) { - constructor(id: Long, baseUrl: String, topic: String, instant: Boolean, mutedUntil: Long, minPriority: Int, autoDelete: Long, lastNotificationId: String, icon: String, upAppId: String, upConnectorToken: String, displayName: String?) : - this(id, baseUrl, topic, instant, mutedUntil, minPriority, autoDelete, lastNotificationId, icon, upAppId, upConnectorToken, displayName, 0, 0, 0, ConnectionState.NOT_APPLICABLE) + constructor(id: Long, baseUrl: String, topic: String, instant: Boolean, mutedUntil: Long, minPriority: Int, autoDelete: Long, lastNotificationId: String, icon: String, upAppId: String, upConnectorToken: String, displayName: String?, ownNotificationChannels: Boolean?) : + this(id, baseUrl, topic, instant, mutedUntil, minPriority, autoDelete, lastNotificationId, icon, upAppId, upConnectorToken, displayName, ownNotificationChannels == true, 0, 0, 0, ConnectionState.NOT_APPLICABLE) } enum class ConnectionState { @@ -49,6 +50,7 @@ data class SubscriptionWithMetadata( val upAppId: String?, val upConnectorToken: String?, val displayName: String?, + val ownNotificationChannels: Boolean, val totalCount: Int, val newCount: Int, val lastActive: Long @@ -158,7 +160,7 @@ data class LogEntry( this(0, timestamp, tag, level, message, exception) } -@androidx.room.Database(entities = [Subscription::class, Notification::class, User::class, LogEntry::class], version = 12) +@androidx.room.Database(entities = [Subscription::class, Notification::class, User::class, LogEntry::class], version = 13) @TypeConverters(Converters::class) abstract class Database : RoomDatabase() { abstract fun subscriptionDao(): SubscriptionDao @@ -185,6 +187,7 @@ abstract class Database : RoomDatabase() { .addMigrations(MIGRATION_9_10) .addMigrations(MIGRATION_10_11) .addMigrations(MIGRATION_11_12) + .addMigrations(MIGRATION_12_13) .fallbackToDestructiveMigration() .build() this.instance = instance @@ -283,6 +286,12 @@ abstract class Database : RoomDatabase() { db.execSQL("ALTER TABLE Notification ADD COLUMN icon_contentUri TEXT") } } + + private val MIGRATION_12_13 = object : Migration(12, 13) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE Subscription ADD COLUMN ownNotificationChannels INTEGER NOT NULL DEFAULT('0')") + } + } } } @@ -290,7 +299,7 @@ abstract class Database : RoomDatabase() { interface SubscriptionDao { @Query(""" SELECT - s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, + s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, s.ownNotificationChannels, COUNT(n.id) totalCount, COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount, IFNULL(MAX(n.timestamp),0) AS lastActive @@ -303,7 +312,7 @@ interface SubscriptionDao { @Query(""" SELECT - s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, + s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, s.ownNotificationChannels, COUNT(n.id) totalCount, COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount, IFNULL(MAX(n.timestamp),0) AS lastActive @@ -316,7 +325,7 @@ interface SubscriptionDao { @Query(""" SELECT - s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, + s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, s.ownNotificationChannels, COUNT(n.id) totalCount, COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount, IFNULL(MAX(n.timestamp),0) AS lastActive @@ -329,7 +338,7 @@ interface SubscriptionDao { @Query(""" SELECT - s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, + s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, s.ownNotificationChannels, COUNT(n.id) totalCount, COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount, IFNULL(MAX(n.timestamp),0) AS lastActive @@ -342,7 +351,7 @@ interface SubscriptionDao { @Query(""" SELECT - s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, + s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.minPriority, s.autoDelete, s.lastNotificationId, s.icon, s.upAppId, s.upConnectorToken, s.displayName, s.ownNotificationChannels, COUNT(n.id) totalCount, COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount, IFNULL(MAX(n.timestamp),0) AS lastActive diff --git a/app/src/main/java/io/heckel/ntfy/db/Repository.kt b/app/src/main/java/io/heckel/ntfy/db/Repository.kt index cee2985..0949bac 100644 --- a/app/src/main/java/io/heckel/ntfy/db/Repository.kt +++ b/app/src/main/java/io/heckel/ntfy/db/Repository.kt @@ -385,6 +385,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas baseUrl = s.baseUrl, topic = s.topic, instant = s.instant, + ownNotificationChannels = s.ownNotificationChannels, mutedUntil = s.mutedUntil, minPriority = s.minPriority, autoDelete = s.autoDelete, @@ -410,6 +411,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas baseUrl = s.baseUrl, topic = s.topic, instant = s.instant, + ownNotificationChannels = s.ownNotificationChannels, mutedUntil = s.mutedUntil, minPriority = s.minPriority, autoDelete = s.autoDelete, diff --git a/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt index 4fc9ec7..af54347 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt @@ -110,6 +110,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra baseUrl = baseUrl, topic = topic, instant = instant, + ownNotificationChannels = false, mutedUntil = 0, minPriority = Repository.MIN_PRIORITY_USE_GLOBAL, autoDelete = Repository.AUTO_DELETE_USE_GLOBAL, diff --git a/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt index ad928a4..cd3ccb6 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt @@ -452,6 +452,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc baseUrl = baseUrl, topic = topic, instant = instant, + ownNotificationChannels = false, mutedUntil = 0, minPriority = Repository.MIN_PRIORITY_USE_GLOBAL, autoDelete = Repository.AUTO_DELETE_USE_GLOBAL, diff --git a/app/src/main/java/io/heckel/ntfy/up/BroadcastReceiver.kt b/app/src/main/java/io/heckel/ntfy/up/BroadcastReceiver.kt index 9119e15..c192c3c 100644 --- a/app/src/main/java/io/heckel/ntfy/up/BroadcastReceiver.kt +++ b/app/src/main/java/io/heckel/ntfy/up/BroadcastReceiver.kt @@ -72,6 +72,7 @@ class BroadcastReceiver : android.content.BroadcastReceiver() { baseUrl = baseUrl, topic = topic, instant = true, // No Firebase, always instant! + ownNotificationChannels = false, mutedUntil = 0, minPriority = Repository.MIN_PRIORITY_USE_GLOBAL, autoDelete = Repository.AUTO_DELETE_USE_GLOBAL,