Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 73f610af authored by Philipp Heckel's avatar Philipp Heckel
Browse files

Full end to end use case works; still ugly though

parent 7dbbf12c
Loading
Loading
Loading
Loading
+13 −5
Original line number Diff line number Diff line
@@ -2,11 +2,11 @@
  "formatVersion": 1,
  "database": {
    "version": 5,
    "identityHash": "d72d045ad4ad20db887b4c6aed3da27b",
    "identityHash": "306578182c2ad0f9803956beda094d28",
    "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 NOT NULL, `upConnectorToken` TEXT NOT NULL, 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, `upAppId` TEXT, `upConnectorToken` TEXT, PRIMARY KEY(`id`))",
        "fields": [
          {
            "fieldPath": "id",
@@ -42,13 +42,13 @@
            "fieldPath": "upAppId",
            "columnName": "upAppId",
            "affinity": "TEXT",
            "notNull": true
            "notNull": false
          },
          {
            "fieldPath": "upConnectorToken",
            "columnName": "upConnectorToken",
            "affinity": "TEXT",
            "notNull": true
            "notNull": false
          }
        ],
        "primaryKey": {
@@ -66,6 +66,14 @@
              "topic"
            ],
            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Subscription_baseUrl_topic` ON `${TABLE_NAME}` (`baseUrl`, `topic`)"
          },
          {
            "name": "index_Subscription_upConnectorToken",
            "unique": true,
            "columnNames": [
              "upConnectorToken"
            ],
            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Subscription_upConnectorToken` ON `${TABLE_NAME}` (`upConnectorToken`)"
          }
        ],
        "foreignKeys": []
@@ -144,7 +152,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, 'd72d045ad4ad20db887b4c6aed3da27b')"
      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '306578182c2ad0f9803956beda094d28')"
    ]
  }
}
 No newline at end of file
+19 −6
Original line number Diff line number Diff line
@@ -6,15 +6,15 @@ import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import kotlinx.coroutines.flow.Flow

@Entity(indices = [Index(value = ["baseUrl", "topic"], unique = true)])
@Entity(indices = [Index(value = ["baseUrl", "topic"], unique = true), Index(value = ["upConnectorToken"], unique = true)])
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,
    @ColumnInfo(name = "instant") val instant: Boolean,
    @ColumnInfo(name = "mutedUntil") val mutedUntil: Long, // TODO notificationSound, notificationSchedule
    @ColumnInfo(name = "upAppId") val upAppId: String,
    @ColumnInfo(name = "upConnectorToken") val upConnectorToken: String,
    @ColumnInfo(name = "upAppId") val upAppId: String?,
    @ColumnInfo(name = "upConnectorToken") val upConnectorToken: String?,
    @Ignore val totalCount: Int = 0, // Total notifications
    @Ignore val newCount: Int = 0, // New notifications
    @Ignore val lastActive: Long = 0, // Unix timestamp
@@ -110,8 +110,8 @@ abstract class Database : RoomDatabase() {

        private val MIGRATION_4_5 = object : Migration(3, 4) {
            override fun migrate(db: SupportSQLiteDatabase) {
                db.execSQL("ALTER TABLE Subscription ADD COLUMN upAppId TEXT NOT NULL DEFAULT('')")
                db.execSQL("ALTER TABLE Subscription ADD COLUMN upConnectorToken TEXT NOT NULL DEFAULT('')")
                db.execSQL("ALTER TABLE Subscription ADD COLUMN upAppId TEXT")
                db.execSQL("ALTER TABLE Subscription ADD COLUMN upConnectorToken TEXT")
            }
        }
    }
@@ -171,6 +171,19 @@ interface SubscriptionDao {
    """)
    fun get(subscriptionId: Long): SubscriptionWithMetadata?

    @Query("""
        SELECT 
          s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil, s.upAppId, s.upConnectorToken,
          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.upConnectorToken = :connectorToken
        GROUP BY s.id
    """)
    fun getByConnectorToken(connectorToken: String): SubscriptionWithMetadata?

    @Insert
    fun add(subscription: Subscription)

+6 −0
Original line number Diff line number Diff line
@@ -54,6 +54,12 @@ class Repository(private val sharedPrefs: SharedPreferences, private val subscri
        return toSubscription(subscriptionDao.get(baseUrl, topic))
    }

    @Suppress("RedundantSuspendModifier")
    @WorkerThread
    suspend fun getSubscriptionByConnectorToken(connectorToken: String): Subscription? {
        return toSubscription(subscriptionDao.getByConnectorToken(connectorToken))
    }

    @Suppress("RedundantSuspendModifier")
    @WorkerThread
    suspend fun addSubscription(subscription: Subscription) {
+6 −3
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ import io.heckel.ntfy.data.Notification
import io.heckel.ntfy.data.Repository
import io.heckel.ntfy.data.Subscription
import io.heckel.ntfy.up.Distributor
import io.heckel.ntfy.util.safeLet

class NotificationDispatcher(val context: Context, val repository: Repository) {
    private val notifier = NotificationService(context)
@@ -18,8 +19,8 @@ class NotificationDispatcher(val context: Context, val repository: Repository) {
    fun dispatch(subscription: Subscription, notification: Notification) {
        val muted = checkMuted(subscription)
        val notify = checkNotify(subscription, notification, muted)
        val broadcast = subscription.upAppId == ""
        val distribute = subscription.upAppId != ""
        val broadcast = subscription.upAppId == null
        val distribute = subscription.upAppId != null
        if (notify) {
            notifier.send(subscription, notification)
        }
@@ -27,7 +28,9 @@ class NotificationDispatcher(val context: Context, val repository: Repository) {
            broadcaster.send(subscription, notification, muted)
        }
        if (distribute) {
            distributor.sendMessage(subscription.upAppId, subscription.upConnectorToken, notification.message)
            safeLet(subscription.upAppId, subscription.upConnectorToken) { appId, connectorToken ->
                distributor.sendMessage(appId, connectorToken, notification.message)
            }
        }
    }

+40 −20
Original line number Diff line number Diff line
@@ -7,6 +7,8 @@ import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application
import io.heckel.ntfy.data.Subscription
import io.heckel.ntfy.ui.SubscriberManager
import io.heckel.ntfy.util.randomString
import io.heckel.ntfy.util.topicUrlUp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@@ -24,17 +26,23 @@ class BroadcastReceiver : android.content.BroadcastReceiver() {
                    Log.w(TAG, "Trying to register an app without packageName")
                    return
                }

                val baseUrl = context!!.getString(R.string.app_base_url) // FIXME
                val topic = connectorToken // FIXME
                val topic = "up" + randomString(TOPIC_LENGTH)
                val endpoint = topicUrlUp(baseUrl, topic)
                val app = context!!.applicationContext as Application
                val repository = app.repository
                val distributor = Distributor(app)
                GlobalScope.launch(Dispatchers.IO) {
                    val existingSubscription = repository.getSubscriptionByConnectorToken(connectorToken)
                    if (existingSubscription != null) {
                        distributor.sendRegistrationRefused(appId, connectorToken)
                        return@launch
                    }
                    val subscription = Subscription(
                        id = Random.nextLong(),
                        baseUrl = baseUrl,
                        topic = topic,
                    instant = true,
                        instant = true, // No Firebase, always instant!
                        mutedUntil = 0,
                        upAppId = appId,
                        upConnectorToken = connectorToken,
@@ -42,26 +50,38 @@ class BroadcastReceiver : android.content.BroadcastReceiver() {
                        newCount = 0,
                        lastActive = Date().time/1000
                    )
                GlobalScope.launch(Dispatchers.IO) {
                    repository.addSubscription(subscription)
                    val subscriptionIdsWithInstantStatus = repository.getSubscriptionIdsWithInstantStatus()
                    val subscriberManager = SubscriberManager(context!!)
                    val subscriberManager = SubscriberManager(app)
                    subscriberManager.refreshService(subscriptionIdsWithInstantStatus)
                    distributor.sendEndpoint(appId, connectorToken, endpoint)
                }
                distributor.sendEndpoint(appId, connectorToken)
                // XXXXXXXXX
            }
            ACTION_UNREGISTER -> {
                val connectorToken = intent.getStringExtra(EXTRA_TOKEN) ?: ""
                Log.d(TAG, "Unregister: connectorToken=$connectorToken")
                // XXXXXXX
                val distributor = Distributor(context!!)
                distributor.sendUnregistered("org.unifiedpush.example", connectorToken)
                val app = context!!.applicationContext as Application
                val repository = app.repository
                val distributor = Distributor(app)
                GlobalScope.launch(Dispatchers.IO) {
                    val existingSubscription = repository.getSubscriptionByConnectorToken(connectorToken)
                    if (existingSubscription == null) {
                        return@launch
                    }
                    repository.removeSubscription(existingSubscription.id)
                    val subscriptionIdsWithInstantStatus = repository.getSubscriptionIdsWithInstantStatus()
                    val subscriberManager = SubscriberManager(app)
                    subscriberManager.refreshService(subscriptionIdsWithInstantStatus)
                    existingSubscription.upAppId?.let { appId ->
                        distributor.sendUnregistered(appId, connectorToken)
                    }
                }
            }
        }
    }

    companion object {
        private const val TAG = "NtfyUpBroadcastRecv"
        private const val TOPIC_LENGTH = 16
    }
}
Loading