2021-11-24 16:12:51 -05:00
|
|
|
package io.heckel.ntfy.firebase
|
2021-11-11 19:41:29 -05:00
|
|
|
|
2021-12-13 20:54:36 -05:00
|
|
|
import android.content.Intent
|
2021-11-11 19:41:29 -05:00
|
|
|
import android.util.Log
|
|
|
|
import com.google.firebase.messaging.FirebaseMessagingService
|
|
|
|
import com.google.firebase.messaging.RemoteMessage
|
|
|
|
import io.heckel.ntfy.R
|
|
|
|
import io.heckel.ntfy.app.Application
|
|
|
|
import io.heckel.ntfy.data.Notification
|
2021-12-29 21:36:47 +01:00
|
|
|
import io.heckel.ntfy.msg.*
|
2021-12-30 17:00:27 +01:00
|
|
|
import io.heckel.ntfy.service.SubscriberService
|
2021-11-27 16:18:09 -05:00
|
|
|
import io.heckel.ntfy.util.toPriority
|
2021-11-11 19:41:29 -05:00
|
|
|
import kotlinx.coroutines.CoroutineScope
|
|
|
|
import kotlinx.coroutines.SupervisorJob
|
|
|
|
import kotlinx.coroutines.launch
|
2021-11-15 16:24:31 -05:00
|
|
|
import kotlin.random.Random
|
2021-11-11 19:41:29 -05:00
|
|
|
|
|
|
|
class FirebaseService : FirebaseMessagingService() {
|
|
|
|
private val repository by lazy { (application as Application).repository }
|
2021-12-29 21:36:47 +01:00
|
|
|
private val dispatcher by lazy { NotificationDispatcher(this, repository) }
|
2021-11-11 19:41:29 -05:00
|
|
|
private val job = SupervisorJob()
|
2021-12-13 22:00:48 -05:00
|
|
|
private val messenger = FirebaseMessenger()
|
2021-11-11 19:41:29 -05:00
|
|
|
|
|
|
|
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
|
|
|
// We only process data messages
|
|
|
|
if (remoteMessage.data.isEmpty()) {
|
2021-12-13 20:54:36 -05:00
|
|
|
Log.d(TAG, "Discarding unexpected message (1): from=${remoteMessage.from}")
|
2021-11-11 19:41:29 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-13 20:54:36 -05:00
|
|
|
// Dispatch event
|
|
|
|
val data = remoteMessage.data
|
|
|
|
when (data["event"]) {
|
2021-12-13 22:00:48 -05:00
|
|
|
ApiService.EVENT_KEEPALIVE -> handleKeepalive(remoteMessage)
|
2021-12-13 20:54:36 -05:00
|
|
|
ApiService.EVENT_MESSAGE -> handleMessage(remoteMessage)
|
|
|
|
else -> Log.d(TAG, "Discarding unexpected message (2): from=${remoteMessage.from}, data=${data}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-13 22:00:48 -05:00
|
|
|
private fun handleKeepalive(remoteMessage: RemoteMessage) {
|
2021-12-13 20:54:36 -05:00
|
|
|
Log.d(TAG, "Keepalive received, sending auto restart broadcast for foregrounds service")
|
|
|
|
sendBroadcast(Intent(this, SubscriberService.AutoRestartReceiver::class.java)) // Restart it if necessary!
|
2021-12-13 22:00:48 -05:00
|
|
|
val topic = remoteMessage.data["topic"]
|
|
|
|
if (topic != ApiService.CONTROL_TOPIC) {
|
|
|
|
Log.d(TAG, "Keepalive on non-control topic $topic received, subscribing to control topic ${ApiService.CONTROL_TOPIC}")
|
|
|
|
messenger.subscribe(ApiService.CONTROL_TOPIC)
|
|
|
|
}
|
2021-12-13 20:54:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun handleMessage(remoteMessage: RemoteMessage) {
|
2021-11-11 19:41:29 -05:00
|
|
|
val data = remoteMessage.data
|
|
|
|
val id = data["id"]
|
|
|
|
val timestamp = data["time"]?.toLongOrNull()
|
|
|
|
val topic = data["topic"]
|
2021-11-27 16:18:09 -05:00
|
|
|
val title = data["title"]
|
2021-11-11 19:41:29 -05:00
|
|
|
val message = data["message"]
|
2021-11-27 16:18:09 -05:00
|
|
|
val priority = data["priority"]?.toIntOrNull()
|
|
|
|
val tags = data["tags"]
|
2022-01-04 21:10:36 +01:00
|
|
|
val truncated = (data["truncated"] ?: "") == "1"
|
2021-11-11 19:41:29 -05:00
|
|
|
if (id == null || topic == null || message == null || timestamp == null) {
|
2022-01-04 19:59:26 +01:00
|
|
|
Log.d(TAG, "Discarding unexpected message: from=${remoteMessage.from}, fcmprio=${remoteMessage.priority}, fcmprio_orig=${remoteMessage.originalPriority}, data=${data}")
|
2021-11-11 19:41:29 -05:00
|
|
|
return
|
|
|
|
}
|
2022-01-04 19:59:26 +01:00
|
|
|
Log.d(TAG, "Received message: from=${remoteMessage.from}, fcmprio=${remoteMessage.priority}, fcmprio_orig=${remoteMessage.originalPriority}, data=${data}")
|
2021-11-11 19:41:29 -05:00
|
|
|
|
|
|
|
CoroutineScope(job).launch {
|
|
|
|
val baseUrl = getString(R.string.app_base_url) // Everything from Firebase comes from main service URL!
|
|
|
|
|
2022-01-04 21:10:36 +01:00
|
|
|
// Check if notification was truncated and discard if it will (or likely already did) arrive via instant delivery
|
2021-11-11 19:41:29 -05:00
|
|
|
val subscription = repository.getSubscription(baseUrl, topic) ?: return@launch
|
2022-01-04 21:10:36 +01:00
|
|
|
if (truncated && subscription.instant) {
|
|
|
|
Log.d(TAG, "Discarding truncated message that did/will arrive via instant delivery: from=${remoteMessage.from}, fcmprio=${remoteMessage.priority}, fcmprio_orig=${remoteMessage.originalPriority}, data=${data}")
|
|
|
|
return@launch
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add notification
|
2021-11-15 16:24:31 -05:00
|
|
|
val notification = Notification(
|
|
|
|
id = id,
|
|
|
|
subscriptionId = subscription.id,
|
|
|
|
timestamp = timestamp,
|
2021-11-27 16:18:09 -05:00
|
|
|
title = title ?: "",
|
2021-11-15 16:24:31 -05:00
|
|
|
message = message,
|
|
|
|
notificationId = Random.nextInt(),
|
2021-11-27 16:18:09 -05:00
|
|
|
priority = toPriority(priority),
|
2021-11-28 19:28:58 -05:00
|
|
|
tags = tags ?: "",
|
2021-11-15 16:24:31 -05:00
|
|
|
deleted = false
|
|
|
|
)
|
2021-12-29 21:36:47 +01:00
|
|
|
if (repository.addNotification(notification)) {
|
2022-01-04 19:59:26 +01:00
|
|
|
Log.d(TAG, "Dispatching notification for message: from=${remoteMessage.from}, fcmprio=${remoteMessage.priority}, fcmprio_orig=${remoteMessage.originalPriority}, data=${data}")
|
2021-12-29 21:36:47 +01:00
|
|
|
dispatcher.dispatch(subscription, notification)
|
2021-12-11 15:09:07 -05:00
|
|
|
}
|
2021-11-11 19:41:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onNewToken(token: String) {
|
|
|
|
// Called if the FCM registration token is updated
|
|
|
|
// We don't actually use or care about the token, since we're using topics
|
|
|
|
Log.d(TAG, "Registration token was updated: $token")
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onDestroy() {
|
|
|
|
super.onDestroy()
|
|
|
|
job.cancel()
|
|
|
|
}
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
private const val TAG = "NtfyFirebase"
|
|
|
|
}
|
|
|
|
}
|