2021-10-30 14:13:58 +13:00
package io.heckel.ntfy.data
import android.content.Context
import androidx.room.*
2021-11-12 16:14:28 +13:00
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
2021-11-16 10:24:31 +13:00
@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-12 16:14:28 +13:00
) {
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
2021-11-12 16:14:28 +13:00
}
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 ,
2021-11-16 10:24:31 +13:00
val totalCount : Int ,
val newCount : Int ,
2021-11-12 16:14:28 +13:00
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
2021-11-12 16:14:28 +13:00
@ColumnInfo ( name = " message " ) val message : String ,
2021-11-16 10:24:31 +13:00
@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 " )
2021-11-12 16:14:28 +13:00
. 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
}
}
2021-11-12 16:14:28 +13:00
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 " )
2021-11-12 16:14:28 +13:00
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) " )
2021-11-16 10:24:31 +13:00
// Add "notificationId" & "deleted" columns
db . execSQL ( " ALTER TABLE Notification ADD COLUMN notificationId INTEGER NOT NULL DEFAULT('0') " )
2021-11-12 16:14:28 +13:00
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 {
2021-11-16 10:24:31 +13:00
@Query ( """
SELECT
2021-11-22 08:54:13 +13:00
s . id , s . baseUrl , s . topic , s . instant , s . mutedUntil ,
2021-11-16 10:24:31 +13:00
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
""" )
2021-11-12 16:14:28 +13:00
fun listFlow ( ) : Flow < List < SubscriptionWithMetadata > >
2021-11-16 10:24:31 +13:00
@Query ( """
SELECT
2021-11-22 08:54:13 +13:00
s . id , s . baseUrl , s . topic , s . instant , s . mutedUntil ,
2021-11-16 10:24:31 +13:00
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
""" )
2021-11-12 16:14:28 +13:00
fun list ( ) : List < SubscriptionWithMetadata >
2021-11-16 10:24:31 +13:00
@Query ( """
SELECT
2021-11-22 08:54:13 +13:00
s . id , s . baseUrl , s . topic , s . instant , s . mutedUntil ,
2021-11-16 10:24:31 +13:00
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
""" )
2021-11-12 16:14:28 +13:00
fun get ( baseUrl : String , topic : String ) : SubscriptionWithMetadata ?
2021-11-16 10:24:31 +13:00
@Query ( """
SELECT
2021-11-22 08:54:13 +13:00
s . id , s . baseUrl , s . topic , s . instant , s . mutedUntil ,
2021-11-16 10:24:31 +13:00
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
""" )
2021-11-12 16:14:28 +13:00
fun get ( subscriptionId : Long ) : SubscriptionWithMetadata ?
2021-11-12 13:41:29 +13:00
2021-10-30 14:13:58 +13:00
@Insert
fun add ( subscription : Subscription )
2021-11-15 07:54:48 +13:00
@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 {
2021-11-12 16:14:28 +13:00
@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
2021-11-12 16:14:28 +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 )
2021-11-12 13:41:29 +13:00
@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 )
2021-11-16 10:24:31 +13:00
@Update
fun update ( notification : Notification )
2021-11-12 16:14:28 +13:00
@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
}