ntfy-android/app/src/main/java/io/heckel/ntfy/data/Database.kt

186 lines
7.2 KiB
Kotlin
Raw Normal View History

2021-10-30 14:13:58 +13:00
package io.heckel.ntfy.data
import android.content.Context
import androidx.room.*
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
2021-10-30 14:13:58 +13:00
import kotlinx.coroutines.flow.Flow
2021-11-22 08:54:13 +13:00
import java.util.*
2021-10-30 14:13:58 +13:00
2021-10-31 01:57:20 +13:00
@Entity(indices = [Index(value = ["baseUrl", "topic"], unique = true)])
2021-10-30 14:13:58 +13:00
data class Subscription(
@PrimaryKey val id: Long, // Internal ID, only used in Repository and activities
@ColumnInfo(name = "baseUrl") val baseUrl: String,
@ColumnInfo(name = "topic") val topic: String,
2021-11-14 13:26:37 +13:00
@ColumnInfo(name = "instant") val instant: Boolean,
2021-11-23 09:45:43 +13:00
@ColumnInfo(name = "mutedUntil") val mutedUntil: Long, // TODO notificationSound, notificationSchedule
@Ignore val totalCount: Int = 0, // Total notifications
@Ignore val newCount: Int = 0, // New notifications
2021-11-15 15:42:41 +13:00
@Ignore val lastActive: Long = 0, // Unix timestamp
@Ignore val state: ConnectionState = ConnectionState.NOT_APPLICABLE
) {
2021-11-22 08:54:13 +13:00
constructor(id: Long, baseUrl: String, topic: String, instant: Boolean, mutedUntil: Long) :
this(id, baseUrl, topic, instant, mutedUntil, 0, 0, 0, ConnectionState.NOT_APPLICABLE)
2021-11-15 15:42:41 +13:00
}
enum class ConnectionState {
2021-11-17 08:08:52 +13:00
NOT_APPLICABLE, CONNECTING, CONNECTED
}
data class SubscriptionWithMetadata(
val id: Long,
val baseUrl: String,
val topic: String,
2021-11-14 13:26:37 +13:00
val instant: Boolean,
2021-11-22 08:54:13 +13:00
val mutedUntil: Long,
val totalCount: Int,
val newCount: Int,
val lastActive: Long
2021-10-30 14:13:58 +13:00
)
2021-11-01 08:19:25 +13:00
@Entity
data class Notification(
2021-11-02 03:49:52 +13:00
@PrimaryKey val id: String,
2021-11-01 08:19:25 +13:00
@ColumnInfo(name = "subscriptionId") val subscriptionId: Long,
@ColumnInfo(name = "timestamp") val timestamp: Long, // Unix timestamp
@ColumnInfo(name = "message") val message: String,
@ColumnInfo(name = "notificationId") val notificationId: Int, // Android notification popup ID
@ColumnInfo(name = "deleted") val deleted: Boolean,
2021-11-01 08:19:25 +13:00
)
2021-11-22 08:54:13 +13:00
@androidx.room.Database(entities = [Subscription::class, Notification::class], version = 3)
2021-10-30 14:13:58 +13:00
abstract class Database : RoomDatabase() {
abstract fun subscriptionDao(): SubscriptionDao
2021-11-01 08:19:25 +13:00
abstract fun notificationDao(): NotificationDao
2021-10-30 14:13:58 +13:00
companion object {
@Volatile
private var instance: Database? = null
fun getInstance(context: Context): Database {
return instance ?: synchronized(this) {
val instance = Room
.databaseBuilder(context.applicationContext, Database::class.java,"AppDatabase")
.addMigrations(MIGRATION_1_2)
2021-11-24 16:38:31 +13:00
.addMigrations(MIGRATION_2_3)
2021-10-30 14:13:58 +13:00
.fallbackToDestructiveMigration()
.build()
this.instance = instance
instance
}
}
private val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
// Drop "notifications" & "lastActive" columns (SQLite does not support dropping columns, ...)
2021-11-14 13:26:37 +13:00
db.execSQL("CREATE TABLE Subscription_New (id INTEGER NOT NULL, baseUrl TEXT NOT NULL, topic TEXT NOT NULL, instant INTEGER NOT NULL DEFAULT('0'), PRIMARY KEY(id))")
2021-11-17 08:35:03 +13:00
db.execSQL("INSERT INTO Subscription_New SELECT id, baseUrl, topic, 0 FROM Subscription")
db.execSQL("DROP TABLE Subscription")
db.execSQL("ALTER TABLE Subscription_New RENAME TO Subscription")
db.execSQL("CREATE UNIQUE INDEX index_Subscription_baseUrl_topic ON Subscription (baseUrl, topic)")
// Add "notificationId" & "deleted" columns
db.execSQL("ALTER TABLE Notification ADD COLUMN notificationId INTEGER NOT NULL DEFAULT('0')")
db.execSQL("ALTER TABLE Notification ADD COLUMN deleted INTEGER NOT NULL DEFAULT('0')")
}
}
2021-11-22 08:54:13 +13:00
private val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Subscription ADD COLUMN mutedUntil INTEGER NOT NULL DEFAULT('0')")
}
}
2021-10-30 14:13:58 +13:00
}
}
@Dao
interface SubscriptionDao {
@Query("""
SELECT
2021-11-22 08:54:13 +13:00
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil,
COUNT(n.id) totalCount,
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
IFNULL(MAX(n.timestamp),0) AS lastActive
FROM Subscription AS s
LEFT JOIN Notification AS n ON s.id=n.subscriptionId AND n.deleted != 1
GROUP BY s.id
ORDER BY MAX(n.timestamp) DESC
""")
fun listFlow(): Flow<List<SubscriptionWithMetadata>>
@Query("""
SELECT
2021-11-22 08:54:13 +13:00
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil,
COUNT(n.id) totalCount,
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
IFNULL(MAX(n.timestamp),0) AS lastActive
FROM Subscription AS s
LEFT JOIN Notification AS n ON s.id=n.subscriptionId AND n.deleted != 1
GROUP BY s.id
ORDER BY MAX(n.timestamp) DESC
""")
fun list(): List<SubscriptionWithMetadata>
@Query("""
SELECT
2021-11-22 08:54:13 +13:00
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil,
COUNT(n.id) totalCount,
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
IFNULL(MAX(n.timestamp),0) AS lastActive
FROM Subscription AS s
LEFT JOIN Notification AS n ON s.id=n.subscriptionId AND n.deleted != 1
WHERE s.baseUrl = :baseUrl AND s.topic = :topic
GROUP BY s.id
""")
fun get(baseUrl: String, topic: String): SubscriptionWithMetadata?
@Query("""
SELECT
2021-11-22 08:54:13 +13:00
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil,
COUNT(n.id) totalCount,
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
IFNULL(MAX(n.timestamp),0) AS lastActive
FROM Subscription AS s
LEFT JOIN Notification AS n ON s.id=n.subscriptionId AND n.deleted != 1
WHERE s.id = :subscriptionId
GROUP BY s.id
""")
fun get(subscriptionId: Long): SubscriptionWithMetadata?
2021-10-30 14:13:58 +13:00
@Insert
fun add(subscription: Subscription)
@Update
fun update(subscription: Subscription)
2021-11-01 08:19:25 +13:00
@Query("DELETE FROM subscription WHERE id = :subscriptionId")
fun remove(subscriptionId: Long)
}
@Dao
interface NotificationDao {
@Query("SELECT * FROM notification WHERE subscriptionId = :subscriptionId AND deleted != 1 ORDER BY timestamp DESC")
2021-11-23 09:45:43 +13:00
fun listFlow(subscriptionId: Long): Flow<List<Notification>>
2021-11-01 08:19:25 +13:00
@Query("SELECT id FROM notification WHERE subscriptionId = :subscriptionId") // Includes deleted
2021-11-08 07:13:32 +13:00
fun listIds(subscriptionId: Long): List<String>
2021-11-11 15:16:00 +13:00
@Insert(onConflict = OnConflictStrategy.IGNORE)
2021-11-01 08:19:25 +13:00
fun add(notification: Notification)
@Query("SELECT * FROM notification WHERE id = :notificationId")
fun get(notificationId: String): Notification?
2021-11-23 09:45:43 +13:00
@Query("UPDATE notification SET notificationId = 0 WHERE subscriptionId = :subscriptionId")
fun clearAllNotificationIds(subscriptionId: Long)
@Update
fun update(notification: Notification)
@Query("UPDATE notification SET deleted = 1 WHERE id = :notificationId")
2021-11-04 06:56:08 +13:00
fun remove(notificationId: String)
2021-11-01 08:19:25 +13:00
@Query("DELETE FROM notification WHERE subscriptionId = :subscriptionId")
fun removeAll(subscriptionId: Long)
2021-10-30 14:13:58 +13:00
}