Skip to content

Commit 6367f1f

Browse files
notif: Add messaging-style notifications support to Pigeon bindings
1 parent 6830e57 commit 6367f1f

File tree

6 files changed

+438
-8
lines changed

6 files changed

+438
-8
lines changed

android/app/src/main/kotlin/com/zulip/flutter/Notifications.g.kt

Lines changed: 147 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,102 @@ data class InboxStyle (
107107
)
108108
}
109109
}
110+
111+
/**
112+
* Corresponds to `androidx.core.app.Person`
113+
*
114+
* See: https://developer.android.com/reference/androidx/core/app/Person
115+
*
116+
* Generated class from Pigeon that represents data sent in messages.
117+
*/
118+
data class Person (
119+
val iconData: ByteArray? = null,
120+
val key: String,
121+
val name: String
122+
123+
) {
124+
companion object {
125+
@Suppress("LocalVariableName")
126+
fun fromList(__pigeon_list: List<Any?>): Person {
127+
val iconData = __pigeon_list[0] as ByteArray?
128+
val key = __pigeon_list[1] as String
129+
val name = __pigeon_list[2] as String
130+
return Person(iconData, key, name)
131+
}
132+
}
133+
fun toList(): List<Any?> {
134+
return listOf<Any?>(
135+
iconData,
136+
key,
137+
name,
138+
)
139+
}
140+
}
141+
142+
/**
143+
* Corresponds to `androidx.core.app.NotificationCompat.MessagingStyle.Message`
144+
*
145+
* See: https://developer.android.com/reference/androidx/core/app/NotificationCompat.MessagingStyle.Message
146+
*
147+
* Generated class from Pigeon that represents data sent in messages.
148+
*/
149+
data class MessagingStyleMessage (
150+
val text: String,
151+
val timestampMs: Long,
152+
val person: Person
153+
154+
) {
155+
companion object {
156+
@Suppress("LocalVariableName")
157+
fun fromList(__pigeon_list: List<Any?>): MessagingStyleMessage {
158+
val text = __pigeon_list[0] as String
159+
val timestampMs = __pigeon_list[1].let { num -> if (num is Int) num.toLong() else num as Long }
160+
val person = __pigeon_list[2] as Person
161+
return MessagingStyleMessage(text, timestampMs, person)
162+
}
163+
}
164+
fun toList(): List<Any?> {
165+
return listOf<Any?>(
166+
text,
167+
timestampMs,
168+
person,
169+
)
170+
}
171+
}
172+
173+
/**
174+
* Corresponds to `androidx.core.app.NotificationCompat.MessagingStyle`
175+
*
176+
* See: https://developer.android.com/reference/androidx/core/app/NotificationCompat.MessagingStyle
177+
*
178+
* Generated class from Pigeon that represents data sent in messages.
179+
*/
180+
data class MessagingStyle (
181+
val user: Person,
182+
val conversationTitle: String? = null,
183+
val isGroupConversation: Boolean,
184+
val messages: List<MessagingStyleMessage?>? = null
185+
186+
) {
187+
companion object {
188+
@Suppress("LocalVariableName")
189+
fun fromList(__pigeon_list: List<Any?>): MessagingStyle {
190+
val user = __pigeon_list[0] as Person
191+
val conversationTitle = __pigeon_list[1] as String?
192+
val isGroupConversation = __pigeon_list[2] as Boolean
193+
val messages = __pigeon_list[3] as List<MessagingStyleMessage?>?
194+
return MessagingStyle(user, conversationTitle, isGroupConversation, messages)
195+
}
196+
}
197+
fun toList(): List<Any?> {
198+
return listOf<Any?>(
199+
user,
200+
conversationTitle,
201+
isGroupConversation,
202+
messages,
203+
)
204+
}
205+
}
110206
private object AndroidNotificationHostApiCodec : StandardMessageCodec() {
111207
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
112208
return when (type) {
@@ -116,10 +212,25 @@ private object AndroidNotificationHostApiCodec : StandardMessageCodec() {
116212
}
117213
}
118214
129.toByte() -> {
215+
return (readValue(buffer) as? List<Any?>)?.let {
216+
MessagingStyle.fromList(it)
217+
}
218+
}
219+
130.toByte() -> {
220+
return (readValue(buffer) as? List<Any?>)?.let {
221+
MessagingStyleMessage.fromList(it)
222+
}
223+
}
224+
131.toByte() -> {
119225
return (readValue(buffer) as? List<Any?>)?.let {
120226
PendingIntent.fromList(it)
121227
}
122228
}
229+
132.toByte() -> {
230+
return (readValue(buffer) as? List<Any?>)?.let {
231+
Person.fromList(it)
232+
}
233+
}
123234
else -> super.readValueOfType(type, buffer)
124235
}
125236
}
@@ -129,10 +240,22 @@ private object AndroidNotificationHostApiCodec : StandardMessageCodec() {
129240
stream.write(128)
130241
writeValue(stream, value.toList())
131242
}
132-
is PendingIntent -> {
243+
is MessagingStyle -> {
133244
stream.write(129)
134245
writeValue(stream, value.toList())
135246
}
247+
is MessagingStyleMessage -> {
248+
stream.write(130)
249+
writeValue(stream, value.toList())
250+
}
251+
is PendingIntent -> {
252+
stream.write(131)
253+
writeValue(stream, value.toList())
254+
}
255+
is Person -> {
256+
stream.write(132)
257+
writeValue(stream, value.toList())
258+
}
136259
else -> super.writeValue(stream, value)
137260
}
138261
}
@@ -159,7 +282,8 @@ interface AndroidNotificationHostApi {
159282
* https://developer.android.com/reference/kotlin/android/app/NotificationManager.html#notify
160283
* https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder
161284
*/
162-
fun notify(tag: String?, id: Long, channelId: String, color: Long?, contentIntent: PendingIntent?, contentText: String?, contentTitle: String?, extras: Map<String?, String?>?, smallIconResourceName: String?, groupKey: String?, isGroupSummary: Boolean?, inboxStyle: InboxStyle?, autoCancel: Boolean?)
285+
fun notify(tag: String?, id: Long, channelId: String, color: Long?, contentIntent: PendingIntent?, contentText: String?, contentTitle: String?, extras: Map<String?, String?>?, smallIconResourceName: String?, groupKey: String?, isGroupSummary: Boolean?, inboxStyle: InboxStyle?, messagingStyle: MessagingStyle?, number: Long?, autoCancel: Boolean?)
286+
fun getActiveNotificationMessagingStyleByTag(tag: String): MessagingStyle?
163287

164288
companion object {
165289
/** The codec used by AndroidNotificationHostApi. */
@@ -186,9 +310,11 @@ interface AndroidNotificationHostApi {
186310
val groupKeyArg = args[9] as String?
187311
val isGroupSummaryArg = args[10] as Boolean?
188312
val inboxStyleArg = args[11] as InboxStyle?
189-
val autoCancelArg = args[12] as Boolean?
313+
val messagingStyleArg = args[12] as MessagingStyle?
314+
val numberArg = args[13].let { num -> if (num is Int) num.toLong() else num as Long? }
315+
val autoCancelArg = args[14] as Boolean?
190316
val wrapped: List<Any?> = try {
191-
api.notify(tagArg, idArg, channelIdArg, colorArg, contentIntentArg, contentTextArg, contentTitleArg, extrasArg, smallIconResourceNameArg, groupKeyArg, isGroupSummaryArg, inboxStyleArg, autoCancelArg)
317+
api.notify(tagArg, idArg, channelIdArg, colorArg, contentIntentArg, contentTextArg, contentTitleArg, extrasArg, smallIconResourceNameArg, groupKeyArg, isGroupSummaryArg, inboxStyleArg, messagingStyleArg, numberArg, autoCancelArg)
192318
listOf<Any?>(null)
193319
} catch (exception: Throwable) {
194320
wrapError(exception)
@@ -199,6 +325,23 @@ interface AndroidNotificationHostApi {
199325
channel.setMessageHandler(null)
200326
}
201327
}
328+
run {
329+
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.zulip.AndroidNotificationHostApi.getActiveNotificationMessagingStyleByTag$separatedMessageChannelSuffix", codec)
330+
if (api != null) {
331+
channel.setMessageHandler { message, reply ->
332+
val args = message as List<Any?>
333+
val tagArg = args[0] as String
334+
val wrapped: List<Any?> = try {
335+
listOf<Any?>(api.getActiveNotificationMessagingStyleByTag(tagArg))
336+
} catch (exception: Throwable) {
337+
wrapError(exception)
338+
}
339+
reply.reply(wrapped)
340+
}
341+
} else {
342+
channel.setMessageHandler(null)
343+
}
344+
}
202345
}
203346
}
204347
}

android/app/src/main/kotlin/com/zulip/flutter/ZulipPlugin.kt

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,28 @@ import android.util.Log
88
import androidx.annotation.Keep
99
import androidx.core.app.NotificationCompat
1010
import androidx.core.app.NotificationManagerCompat
11+
import androidx.core.graphics.drawable.IconCompat
1112
import io.flutter.embedding.engine.plugins.FlutterPlugin
1213

1314
private const val TAG = "ZulipPlugin"
1415

16+
fun toAndroidPerson(person: Person): androidx.core.app.Person {
17+
return androidx.core.app.Person.Builder().apply {
18+
person.iconData?.let { setIcon(IconCompat.createWithData(it, 0, it.size)) }
19+
setKey(person.key)
20+
setName(person.name)
21+
}.build()
22+
}
23+
24+
@SuppressLint("RestrictedApi")
25+
fun toPigeonPerson(person: androidx.core.app.Person): Person {
26+
return Person(
27+
null, // TODO: Serialize icon maybe using content URI, mData is not exposed.
28+
person.key!!,
29+
person.name!!.toString(),
30+
)
31+
}
32+
1533
private class AndroidNotificationHost(val context: Context)
1634
: AndroidNotificationHostApi {
1735
@SuppressLint(
@@ -33,6 +51,8 @@ private class AndroidNotificationHost(val context: Context)
3351
groupKey: String?,
3452
isGroupSummary: Boolean?,
3553
inboxStyle: InboxStyle?,
54+
messagingStyle: MessagingStyle?,
55+
number: Long?,
3656
autoCancel: Boolean?
3757
) {
3858
val notification = NotificationCompat.Builder(context, channelId).apply {
@@ -61,10 +81,47 @@ private class AndroidNotificationHost(val context: Context)
6181
NotificationCompat.InboxStyle()
6282
.setSummaryText(it.summaryText)
6383
) }
84+
messagingStyle?.let { messagingStyle ->
85+
val style = NotificationCompat.MessagingStyle(toAndroidPerson(messagingStyle.user))
86+
.setConversationTitle(messagingStyle.conversationTitle)
87+
.setGroupConversation(messagingStyle.isGroupConversation)
88+
messagingStyle.messages?.forEach { it?.let {
89+
style.addMessage(NotificationCompat.MessagingStyle.Message(
90+
it.text,
91+
it.timestampMs,
92+
toAndroidPerson(it.person),
93+
))
94+
} }
95+
setStyle(style)
96+
}
97+
number?.let { setNumber(it.toInt()) }
6498
autoCancel?.let { setAutoCancel(it) }
6599
}.build()
66100
NotificationManagerCompat.from(context).notify(tag, id.toInt(), notification)
67101
}
102+
103+
override fun getActiveNotificationMessagingStyleByTag(tag: String): MessagingStyle? {
104+
val activeNotification = NotificationManagerCompat.from(context)
105+
.activeNotifications
106+
.find { it.tag == tag }
107+
activeNotification?.notification?.let { notification ->
108+
NotificationCompat.MessagingStyle
109+
.extractMessagingStyleFromNotification(notification)
110+
?.let { style ->
111+
return MessagingStyle(
112+
toPigeonPerson(style.user),
113+
style.conversationTitle!!.toString(),
114+
style.isGroupConversation,
115+
style.messages.map { MessagingStyleMessage(
116+
it.text!!.toString(),
117+
it.timestamp,
118+
toPigeonPerson(it.person!!)
119+
) }
120+
)
121+
}
122+
}
123+
return null
124+
}
68125
}
69126

70127
/** A Flutter plugin for the Zulip app's ad-hoc needs. */

0 commit comments

Comments
 (0)