-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathORFirebaseMessagingService.kt
More file actions
255 lines (236 loc) · 10.9 KB
/
ORFirebaseMessagingService.kt
File metadata and controls
255 lines (236 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
package io.openremote.orlib.service
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.media.RingtoneManager
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.google.firebase.messaging.RemoteMessage
import io.openremote.orlib.ORConstants
import io.openremote.orlib.R
import io.openremote.orlib.models.ORAlertAction
import io.openremote.orlib.models.ORAlertButton
import io.openremote.orlib.ui.NotificationActivity
import java.util.logging.Level
import java.util.logging.Logger
class ORFirebaseMessagingService : com.google.firebase.messaging.FirebaseMessagingService() {
private var notificationResource: NotificationResource? = null
class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
LOG.info("TODO: Remove notification")
}
}
override fun onCreate() {
super.onCreate()
notificationResource = NotificationResource(applicationContext)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel()
}
}
/**
* Called when message is received.
*
* @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
*/
// [START receive_message]
override fun onMessageReceived(remoteMessage: RemoteMessage) {
// [START_EXCLUDE]
// There are two types of messages data messages and notification messages. Data messages are handled
// here in onMessageReceived whether the app is in the foreground or background. Data messages are the type
// traditionally used with GCM. Notification messages are only received here in onMessageReceived when the app
// is in the foreground. When the app is in the background an automatically generated notification is displayed.
// When the user taps on the notification they are returned to the app. Messages containing both notification
// and data payloads are treated as notification messages. The Firebase console always sends notification
// messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options
// [END_EXCLUDE]
// TODO(developer): Handle FCM messages here.
// Not getting messages here? See why this may be: https://goo.gl/39bRNJ
LOG.info("Received message from: " + remoteMessage.from)
// If the message contains a notification then we assume it has been shown to the user
if (remoteMessage.notification != null) {
LOG.info(
"Message contains notification body: " + remoteMessage.notification!!.getBody()
)
} else if (remoteMessage.data.isNotEmpty()) {
val messageData: Map<String, String> = remoteMessage.data
// Mark as delivered on the server
val notificationIdStr = messageData["notification-id"]
var notificationId: Long? = null
if (notificationIdStr != null && notificationIdStr.isNotEmpty()) {
notificationId = notificationIdStr.toLong()
val consoleId: String? = getSharedPreferences(
applicationContext.getString(R.string.app_name),
Context.MODE_PRIVATE
).getString("consoleId", "")
if (!consoleId.isNullOrBlank()) {
notificationResource?.notificationDelivered(notificationId, consoleId)
}
}
val isSilent = !messageData.containsKey("or-title")
if (isSilent) {
when (val action: String? = remoteMessage.data.get("action")) {
"GEOFENCE_REFRESH" -> {
val geofenceProvider = GeofenceProvider(applicationContext)
geofenceProvider.refreshGeofences()
}
else -> {
val broadCastIntent = Intent(ORConstants.ACTION_BROADCAST)
broadCastIntent.putExtra("action", action)
sendBroadcast(broadCastIntent)
}
}
} else {
val title = messageData["or-title"]
val body = messageData["or-body"]
var buttonORS: Array<ORAlertButton>? = null
var actionOR: ORAlertAction? = null
// Check for action (to be executed when notification is clicked)
if (messageData.containsKey("action")) {
val actionJson = messageData["action"]
if (actionJson != null && actionJson.isNotEmpty()) {
try {
actionOR = jacksonObjectMapper().readValue(
actionJson,
ORAlertAction::class.java
)
} catch (e: Exception) {
LOG.log(Level.SEVERE, "Failed to de-serialise alert action", e)
}
}
}
// Check for buttons
if (messageData.containsKey("buttons")) {
val buttonsJson = messageData["buttons"]
if (buttonsJson != null && buttonsJson.isNotEmpty()) {
try {
buttonORS = jacksonObjectMapper().readValue(
buttonsJson,
Array<ORAlertButton>::class.java
)
} catch (e: Exception) {
LOG.log(Level.SEVERE, "Failed to de-serialise alert actions", e)
}
}
}
handleNotification(notificationId, title, body, actionOR, buttonORS)
}
}
}
override fun onNewToken(token: String) {
super.onNewToken(token)
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel() {
val channelName = "OR Message Service"
val channel = NotificationChannel(
getString(R.string.NOTIFICATION_CHANNEL_ID),
channelName, NotificationManager.IMPORTANCE_HIGH
)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
channel.setShowBadge(false)
channel.setSound(defaultSoundUri, null)
channel.enableVibration(true)
channel.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
service.createNotificationChannel(channel)
}
private fun handleNotification(
notificationId: Long?,
title: String?,
body: String?,
actionOR: ORAlertAction?,
buttonORS: Array<ORAlertButton>?
) {
val pm = packageManager
val notificationIntent = pm.getLaunchIntentForPackage(packageName)
notificationIntent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val notificationBuilder = NotificationCompat.Builder(
this,
getString(R.string.NOTIFICATION_CHANNEL_ID)
)
.setContentTitle(title)
.setStyle(NotificationCompat.BigTextStyle().bigText(body))
.setContentText(body)
.setSmallIcon(R.drawable.ic_notification)
.setContentIntent(pendingIntent)
.setDeleteIntent(createActionIntent(notificationId, "\"CLOSED\"", null))
if (actionOR != null) {
notificationBuilder.setContentIntent(createActionIntent(notificationId, null, actionOR))
}
if (buttonORS != null) {
for (alertButton in buttonORS) {
notificationBuilder.addAction(
NotificationCompat.Action(
R.drawable.empty,
alertButton.title,
createActionIntent(
notificationId,
alertButton.title,
alertButton.action
)
)
)
}
}
LOG.info(
"Showing notification id=$notificationId, title=$title, body=$body, action=$actionOR, buttons=" + (buttonORS?.size
?: 0)
)
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?
notificationManager?.notify(notificationId.hashCode(), notificationBuilder.build())
}
private fun createActionIntent(
notificationId: Long?,
acknowledgement: String?,
orAlertAction: ORAlertAction?
): PendingIntent {
val actionIntent = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
Intent(this, NotificationActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TASK or
Intent.FLAG_ACTIVITY_NO_HISTORY or
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
}
}
else -> Intent(this, ORMessagingActionService::class.java)
}
actionIntent.putExtra("notificationId", notificationId)
actionIntent.putExtra("acknowledgement", acknowledgement)
actionIntent.action = System.currentTimeMillis().toString()
if (orAlertAction?.url != null && orAlertAction.url.isNotEmpty()) {
actionIntent.putExtra("appUrl", orAlertAction.url)
actionIntent.putExtra("httpMethod", orAlertAction.httpMethod)
actionIntent.putExtra("silent", orAlertAction.silent)
actionIntent.putExtra("openInBrowser", orAlertAction.openInBrowser)
actionIntent.putExtra("data", ObjectMapper().writeValueAsString(orAlertAction.data))
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.getActivity(
this,
0,
actionIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
} else {
PendingIntent.getService(
this,
0,
actionIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
}
}
companion object {
private val LOG = Logger.getLogger(
ORFirebaseMessagingService::class.java.name
)
}
}