Forward messages, don't show ntfy notification
This commit is contained in:
parent
94e595110d
commit
7e9da28704
9 changed files with 104 additions and 119 deletions
|
@ -85,16 +85,13 @@ class Repository(private val sharedPrefs: SharedPreferences, private val subscri
|
|||
|
||||
@Suppress("RedundantSuspendModifier")
|
||||
@WorkerThread
|
||||
suspend fun addNotification(notification: Notification): NotificationAddResult {
|
||||
suspend fun addNotification(notification: Notification): Boolean {
|
||||
val maybeExistingNotification = notificationDao.get(notification.id)
|
||||
if (maybeExistingNotification == null) {
|
||||
notificationDao.add(notification)
|
||||
val detailsVisible = detailViewSubscriptionId.get() == notification.subscriptionId
|
||||
val muted = isMuted(notification.subscriptionId)
|
||||
val notify = !detailsVisible && !muted
|
||||
return NotificationAddResult(notification = notification, notify = notify, broadcast = true, muted = muted)
|
||||
if (maybeExistingNotification != null) {
|
||||
return false
|
||||
}
|
||||
return NotificationAddResult(notification = notification, notify = false, broadcast = false, forward = false, muted = false)
|
||||
notificationDao.add(notification)
|
||||
return true
|
||||
}
|
||||
|
||||
@Suppress("RedundantSuspendModifier")
|
||||
|
@ -141,7 +138,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val subscri
|
|||
return s.mutedUntil == 1L || (s.mutedUntil > 1L && s.mutedUntil > System.currentTimeMillis()/1000)
|
||||
}
|
||||
|
||||
private fun isGlobalMuted(): Boolean {
|
||||
fun isGlobalMuted(): Boolean {
|
||||
val mutedUntil = getGlobalMutedUntil()
|
||||
return mutedUntil == 1L || (mutedUntil > 1L && mutedUntil > System.currentTimeMillis()/1000)
|
||||
}
|
||||
|
@ -228,14 +225,6 @@ class Repository(private val sharedPrefs: SharedPreferences, private val subscri
|
|||
return connectionStatesLiveData.value!!.getOrElse(subscriptionId) { ConnectionState.NOT_APPLICABLE }
|
||||
}
|
||||
|
||||
data class NotificationAddResult(
|
||||
val notification: Notification,
|
||||
val notify: Boolean,
|
||||
val broadcast: Boolean,
|
||||
val forward: Boolean, // Forward to UnifiedPush connector
|
||||
val muted: Boolean,
|
||||
)
|
||||
|
||||
companion object {
|
||||
const val SHARED_PREFS_ID = "MainPreferences"
|
||||
const val SHARED_PREFS_POLL_WORKER_VERSION = "PollWorkerVersion"
|
||||
|
|
|
@ -1,28 +1,49 @@
|
|||
package io.heckel.ntfy.msg
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.TaskStackBuilder
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.RingtoneManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import io.heckel.ntfy.R
|
||||
import io.heckel.ntfy.data.Notification
|
||||
import io.heckel.ntfy.data.Repository
|
||||
import io.heckel.ntfy.data.Subscription
|
||||
import io.heckel.ntfy.ui.DetailActivity
|
||||
import io.heckel.ntfy.ui.MainActivity
|
||||
import io.heckel.ntfy.util.formatMessage
|
||||
import io.heckel.ntfy.util.formatTitle
|
||||
import io.heckel.ntfy.up.Distributor
|
||||
|
||||
class NotificationDispatcher(val context: Context, val repository: Repository) {
|
||||
private val notifier = NotificationService(context)
|
||||
private val broadcaster = BroadcastService(context)
|
||||
private val distributor = Distributor(context)
|
||||
|
||||
fun init() {
|
||||
notifier.createNotificationChannels()
|
||||
}
|
||||
|
||||
class NotificationDispatcher(val context: Context) {
|
||||
fun dispatch(subscription: Subscription, notification: Notification) {
|
||||
val muted = checkMuted(subscription)
|
||||
val notify = checkNotify(subscription, notification, muted)
|
||||
val broadcast = subscription.upAppId == ""
|
||||
val distribute = subscription.upAppId != ""
|
||||
if (notify) {
|
||||
notifier.send(subscription, notification)
|
||||
}
|
||||
if (broadcast) {
|
||||
broadcaster.send(subscription, notification, muted)
|
||||
}
|
||||
if (distribute) {
|
||||
distributor.sendMessage(subscription.upAppId, subscription.upConnectorToken, notification.message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkNotify(subscription: Subscription, notification: Notification, muted: Boolean): Boolean {
|
||||
if (subscription.upAppId != "") {
|
||||
return false
|
||||
}
|
||||
val detailsVisible = repository.detailViewSubscriptionId.get() == notification.subscriptionId
|
||||
return !detailsVisible && !muted
|
||||
}
|
||||
|
||||
private fun checkMuted(subscription: Subscription): Boolean {
|
||||
if (repository.isGlobalMuted()) {
|
||||
return true
|
||||
}
|
||||
return subscription.mutedUntil == 1L || (subscription.mutedUntil > 1L && subscription.mutedUntil > System.currentTimeMillis()/1000)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -58,10 +58,9 @@ class SubscriberService : Service() {
|
|||
private var wakeLock: PowerManager.WakeLock? = null
|
||||
private var isServiceStarted = false
|
||||
private val repository by lazy { (application as Application).repository }
|
||||
private val dispatcher by lazy { NotificationDispatcher(this, repository) }
|
||||
private val connections = ConcurrentHashMap<String, SubscriberConnection>() // Base URL -> Connection
|
||||
private val api = ApiService()
|
||||
private val notifier = NotificationService(this)
|
||||
private val broadcaster = BroadcastService(this)
|
||||
private var notificationManager: NotificationManager? = null
|
||||
private var serviceNotification: Notification? = null
|
||||
|
||||
|
@ -201,18 +200,13 @@ class SubscriberService : Service() {
|
|||
repository.updateState(subscriptionIds, state)
|
||||
}
|
||||
|
||||
private fun onNotificationReceived(subscription: Subscription, n: io.heckel.ntfy.data.Notification) {
|
||||
private fun onNotificationReceived(subscription: Subscription, notification: io.heckel.ntfy.data.Notification) {
|
||||
val url = topicUrl(subscription.baseUrl, subscription.topic)
|
||||
Log.d(TAG, "[$url] Received notification: $n")
|
||||
Log.d(TAG, "[$url] Received notification: $notification")
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
val result = repository.addNotification(n)
|
||||
if (result.notify) {
|
||||
Log.d(TAG, "[$url] Showing notification: $n")
|
||||
notifier.send(subscription, n)
|
||||
}
|
||||
if (result.broadcast) {
|
||||
Log.d(TAG, "[$url] Broadcasting notification: $n")
|
||||
broadcaster.send(subscription, n, result.muted)
|
||||
if (repository.addNotification(notification)) {
|
||||
Log.d(TAG, "[$url] Dispatching notification $notification")
|
||||
dispatcher.dispatch(subscription, notification)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,8 +52,6 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
|
|||
private var actionMode: ActionMode? = null
|
||||
private var workManager: WorkManager? = null // Context-dependent
|
||||
private var dispatcher: NotificationDispatcher? = null // Context-dependent
|
||||
private var notifier: NotificationService? = null // Context-dependent
|
||||
private var broadcaster: BroadcastService? = null // Context-dependent
|
||||
private var subscriberManager: SubscriberManager? = null // Context-dependent
|
||||
private var appBaseUrl: String? = null // Context-dependent
|
||||
|
||||
|
@ -65,9 +63,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
|
|||
|
||||
// Dependencies that depend on Context
|
||||
workManager = WorkManager.getInstance(this)
|
||||
dispatcher = NotificationDispatcher(this)
|
||||
notifier = NotificationService(this)
|
||||
broadcaster = BroadcastService(this)
|
||||
dispatcher = NotificationDispatcher(this, repository)
|
||||
subscriberManager = SubscriberManager(this)
|
||||
appBaseUrl = getString(R.string.app_base_url)
|
||||
|
||||
|
@ -113,7 +109,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
|
|||
}
|
||||
|
||||
// Create notification channels right away, so we can configure them immediately after installing the app
|
||||
notifier!!.createNotificationChannels()
|
||||
dispatcher?.init()
|
||||
|
||||
// Subscribe to control Firebase channel (so we can re-start the foreground service if it dies)
|
||||
messenger.subscribe(ApiService.CONTROL_TOPIC)
|
||||
|
@ -342,13 +338,8 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
|
|||
newNotifications.forEach { notification ->
|
||||
newNotificationsCount++
|
||||
val notificationWithId = notification.copy(notificationId = Random.nextInt())
|
||||
val result = repository.addNotification(notificationWithId)
|
||||
dispatcher?.dispatch()
|
||||
if (result.notify) {
|
||||
notifier?.send(subscription, notificationWithId)
|
||||
}
|
||||
if (result.broadcast) {
|
||||
broadcaster?.send(subscription, notification, result.muted)
|
||||
if (repository.addNotification(notificationWithId)) {
|
||||
dispatcher?.dispatch(subscription, notificationWithId)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
|
|
@ -28,6 +28,7 @@ class BroadcastReceiver : android.content.BroadcastReceiver() {
|
|||
val topic = connectorToken // FIXME
|
||||
val app = context!!.applicationContext as Application
|
||||
val repository = app.repository
|
||||
val distributor = Distributor(app)
|
||||
val subscription = Subscription(
|
||||
id = Random.nextLong(),
|
||||
baseUrl = baseUrl,
|
||||
|
@ -44,14 +45,15 @@ class BroadcastReceiver : android.content.BroadcastReceiver() {
|
|||
repository.addSubscription(subscription)
|
||||
}
|
||||
|
||||
sendEndpoint(context!!, appId, connectorToken)
|
||||
distributor.sendEndpoint(appId, connectorToken)
|
||||
// XXXXXXXXX
|
||||
}
|
||||
ACTION_UNREGISTER -> {
|
||||
val connectorToken = intent.getStringExtra(EXTRA_TOKEN) ?: ""
|
||||
Log.d(TAG, "Unregister: connectorToken=$connectorToken")
|
||||
// XXXXXXX
|
||||
sendUnregistered(context!!, "org.unifiedpush.example", connectorToken)
|
||||
val distributor = Distributor(context!!)
|
||||
distributor.sendUnregistered("org.unifiedpush.example", connectorToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
36
app/src/main/java/io/heckel/ntfy/up/Distributor.kt
Normal file
36
app/src/main/java/io/heckel/ntfy/up/Distributor.kt
Normal file
|
@ -0,0 +1,36 @@
|
|||
package io.heckel.ntfy.up
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import io.heckel.ntfy.R
|
||||
import io.heckel.ntfy.data.Repository
|
||||
import io.heckel.ntfy.util.topicUrlUp
|
||||
|
||||
class Distributor(val context: Context) {
|
||||
fun sendMessage(app: String, token: String, message: String) {
|
||||
val broadcastIntent = Intent()
|
||||
broadcastIntent.`package` = app
|
||||
broadcastIntent.action = ACTION_MESSAGE
|
||||
broadcastIntent.putExtra(EXTRA_TOKEN, token)
|
||||
broadcastIntent.putExtra(EXTRA_MESSAGE, message)
|
||||
context.sendBroadcast(broadcastIntent)
|
||||
}
|
||||
|
||||
fun sendEndpoint(app: String, token: String) {
|
||||
val appBaseUrl = context.getString(R.string.app_base_url) // FIXME
|
||||
val broadcastIntent = Intent()
|
||||
broadcastIntent.`package` = app
|
||||
broadcastIntent.action = ACTION_NEW_ENDPOINT
|
||||
broadcastIntent.putExtra(EXTRA_TOKEN, token)
|
||||
broadcastIntent.putExtra(EXTRA_ENDPOINT, topicUrlUp(appBaseUrl, token))
|
||||
context.sendBroadcast(broadcastIntent)
|
||||
}
|
||||
|
||||
fun sendUnregistered(app: String, token: String) {
|
||||
val broadcastIntent = Intent()
|
||||
broadcastIntent.`package` = app
|
||||
broadcastIntent.action = ACTION_UNREGISTERED
|
||||
broadcastIntent.putExtra(EXTRA_TOKEN, token)
|
||||
context.sendBroadcast(broadcastIntent)
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package io.heckel.ntfy.up
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import io.heckel.ntfy.R
|
||||
import io.heckel.ntfy.util.topicUrlUp
|
||||
|
||||
fun sendMessage(context: Context, app: String, token: String, message: String) {
|
||||
val broadcastIntent = Intent()
|
||||
broadcastIntent.`package` = app
|
||||
broadcastIntent.action = ACTION_MESSAGE
|
||||
broadcastIntent.putExtra(EXTRA_TOKEN, token)
|
||||
broadcastIntent.putExtra(EXTRA_MESSAGE, message)
|
||||
context.sendBroadcast(broadcastIntent)
|
||||
}
|
||||
|
||||
fun sendEndpoint(context: Context, app: String, token: String) {
|
||||
val appBaseUrl = context.getString(R.string.app_base_url)
|
||||
val broadcastIntent = Intent()
|
||||
broadcastIntent.`package` = app
|
||||
broadcastIntent.action = ACTION_NEW_ENDPOINT
|
||||
broadcastIntent.putExtra(EXTRA_TOKEN, token)
|
||||
broadcastIntent.putExtra(EXTRA_ENDPOINT, topicUrlUp(appBaseUrl, token))
|
||||
context.sendBroadcast(broadcastIntent)
|
||||
}
|
||||
|
||||
fun sendUnregistered(context: Context, app: String, token: String) {
|
||||
val broadcastIntent = Intent()
|
||||
broadcastIntent.`package` = app
|
||||
broadcastIntent.action = ACTION_UNREGISTERED
|
||||
broadcastIntent.putExtra(EXTRA_TOKEN, token)
|
||||
context.sendBroadcast(broadcastIntent)
|
||||
}
|
||||
|
|
@ -7,8 +7,10 @@ import androidx.work.WorkerParameters
|
|||
import io.heckel.ntfy.BuildConfig
|
||||
import io.heckel.ntfy.data.Database
|
||||
import io.heckel.ntfy.data.Repository
|
||||
import io.heckel.ntfy.firebase.FirebaseService
|
||||
import io.heckel.ntfy.msg.ApiService
|
||||
import io.heckel.ntfy.msg.BroadcastService
|
||||
import io.heckel.ntfy.msg.NotificationDispatcher
|
||||
import io.heckel.ntfy.msg.NotificationService
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -25,8 +27,7 @@ class PollWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx,
|
|||
val database = Database.getInstance(applicationContext)
|
||||
val sharedPrefs = applicationContext.getSharedPreferences(Repository.SHARED_PREFS_ID, Context.MODE_PRIVATE)
|
||||
val repository = Repository.getInstance(sharedPrefs, database.subscriptionDao(), database.notificationDao())
|
||||
val notifier = NotificationService(applicationContext)
|
||||
val broadcaster = BroadcastService(applicationContext)
|
||||
val dispatcher = NotificationDispatcher(applicationContext, repository)
|
||||
val api = ApiService()
|
||||
|
||||
repository.getSubscriptions().forEach{ subscription ->
|
||||
|
@ -36,12 +37,8 @@ class PollWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx,
|
|||
.onlyNewNotifications(subscription.id, notifications)
|
||||
.map { it.copy(notificationId = Random.nextInt()) }
|
||||
newNotifications.forEach { notification ->
|
||||
val result = repository.addNotification(notification)
|
||||
if (result.notify) {
|
||||
notifier.send(subscription, notification)
|
||||
}
|
||||
if (result.broadcast) {
|
||||
broadcaster.send(subscription, notification, result.muted)
|
||||
if (repository.addNotification(notification)) {
|
||||
dispatcher.dispatch(subscription, notification)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
|
|
@ -7,10 +7,7 @@ import com.google.firebase.messaging.RemoteMessage
|
|||
import io.heckel.ntfy.R
|
||||
import io.heckel.ntfy.app.Application
|
||||
import io.heckel.ntfy.data.Notification
|
||||
import io.heckel.ntfy.msg.ApiService
|
||||
import io.heckel.ntfy.msg.BroadcastService
|
||||
import io.heckel.ntfy.msg.NotificationService
|
||||
import io.heckel.ntfy.msg.SubscriberService
|
||||
import io.heckel.ntfy.msg.*
|
||||
import io.heckel.ntfy.util.toPriority
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
|
@ -19,9 +16,8 @@ import kotlin.random.Random
|
|||
|
||||
class FirebaseService : FirebaseMessagingService() {
|
||||
private val repository by lazy { (application as Application).repository }
|
||||
private val dispatcher by lazy { NotificationDispatcher(this, repository) }
|
||||
private val job = SupervisorJob()
|
||||
private val notifier = NotificationService(this)
|
||||
private val broadcaster = BroadcastService(this)
|
||||
private val messenger = FirebaseMessenger()
|
||||
|
||||
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
||||
|
@ -81,16 +77,9 @@ class FirebaseService : FirebaseMessagingService() {
|
|||
tags = tags ?: "",
|
||||
deleted = false
|
||||
)
|
||||
val result = repository.addNotification(notification)
|
||||
|
||||
// Send notification (only if it's not already known)
|
||||
if (result.notify) {
|
||||
Log.d(TAG, "Sending notification for message: from=${remoteMessage.from}, data=${data}")
|
||||
notifier.send(subscription, notification)
|
||||
}
|
||||
if (result.broadcast) {
|
||||
Log.d(TAG, "Sending broadcast for message: from=${remoteMessage.from}, data=${data}")
|
||||
broadcaster.send(subscription, notification, result.muted)
|
||||
if (repository.addNotification(notification)) {
|
||||
Log.d(TAG, "Dispatching notification for message: from=${remoteMessage.from}, data=${data}")
|
||||
dispatcher.dispatch(subscription, notification)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue