Skip to content

Commit d8033e5

Browse files
notif: Add messaging-style notifications support to Pigeon bindings
Add methods and types for creating messaging style notifications: https://developer.android.com/develop/ui/views/notifications/build-notification#messaging-style
1 parent 1d24f6d commit d8033e5

File tree

5 files changed

+414
-5
lines changed

5 files changed

+414
-5
lines changed

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

Lines changed: 146 additions & 3 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(
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(
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 messages: List<MessagingStyleMessage?>? = null,
184+
val isGroupConversation: Boolean
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 messages = __pigeon_list[2] as List<MessagingStyleMessage?>?
193+
val isGroupConversation = __pigeon_list[3] as Boolean
194+
return MessagingStyle(user, conversationTitle, messages, isGroupConversation)
195+
}
196+
}
197+
fun toList(): List<Any?> {
198+
return listOf(
199+
user,
200+
conversationTitle,
201+
messages,
202+
isGroupConversation,
203+
)
204+
}
205+
}
110206
private object NotificationsPigeonCodec : StandardMessageCodec() {
111207
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
112208
return when (type) {
@@ -120,6 +216,21 @@ private object NotificationsPigeonCodec : StandardMessageCodec() {
120216
InboxStyle.fromList(it)
121217
}
122218
}
219+
131.toByte() -> {
220+
return (readValue(buffer) as? List<Any?>)?.let {
221+
Person.fromList(it)
222+
}
223+
}
224+
132.toByte() -> {
225+
return (readValue(buffer) as? List<Any?>)?.let {
226+
MessagingStyleMessage.fromList(it)
227+
}
228+
}
229+
133.toByte() -> {
230+
return (readValue(buffer) as? List<Any?>)?.let {
231+
MessagingStyle.fromList(it)
232+
}
233+
}
123234
else -> super.readValueOfType(type, buffer)
124235
}
125236
}
@@ -133,6 +244,18 @@ private object NotificationsPigeonCodec : StandardMessageCodec() {
133244
stream.write(130)
134245
writeValue(stream, value.toList())
135246
}
247+
is Person -> {
248+
stream.write(131)
249+
writeValue(stream, value.toList())
250+
}
251+
is MessagingStyleMessage -> {
252+
stream.write(132)
253+
writeValue(stream, value.toList())
254+
}
255+
is MessagingStyle -> {
256+
stream.write(133)
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, autoCancel: Boolean?, channelId: String, color: Long?, contentIntent: PendingIntent?, contentText: String?, contentTitle: String?, extras: Map<String?, String?>?, groupKey: String?, inboxStyle: InboxStyle?, isGroupSummary: Boolean?, smallIconResourceName: String?)
285+
fun notify(tag: String?, id: Long, autoCancel: Boolean?, channelId: String, color: Long?, contentIntent: PendingIntent?, contentText: String?, contentTitle: String?, extras: Map<String?, String?>?, groupKey: String?, inboxStyle: InboxStyle?, isGroupSummary: Boolean?, messagingStyle: MessagingStyle?, number: Long?, smallIconResourceName: String?)
286+
fun getActiveNotificationMessagingStyleByTag(tag: String): MessagingStyle?
163287

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

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,27 @@ 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+
fun toPigeonPerson(person: androidx.core.app.Person): Person {
25+
return Person(
26+
null, // TODO: Serialize icon maybe using content URI, mData is not exposed.
27+
person.key!!,
28+
person.name!!.toString(),
29+
)
30+
}
31+
1532
private class AndroidNotificationHost(val context: Context)
1633
: AndroidNotificationHostApi {
1734
@SuppressLint(
@@ -33,6 +50,8 @@ private class AndroidNotificationHost(val context: Context)
3350
groupKey: String?,
3451
inboxStyle: InboxStyle?,
3552
isGroupSummary: Boolean?,
53+
messagingStyle: MessagingStyle?,
54+
number: Long?,
3655
smallIconResourceName: String?
3756
) {
3857
val notification = NotificationCompat.Builder(context, channelId).apply {
@@ -60,11 +79,48 @@ private class AndroidNotificationHost(val context: Context)
6079
.setSummaryText(it.summaryText)
6180
) }
6281
isGroupSummary?.let { setGroupSummary(it) }
82+
messagingStyle?.let { messagingStyle ->
83+
val style = NotificationCompat.MessagingStyle(toAndroidPerson(messagingStyle.user))
84+
.setConversationTitle(messagingStyle.conversationTitle)
85+
.setGroupConversation(messagingStyle.isGroupConversation)
86+
messagingStyle.messages?.forEach { it?.let {
87+
style.addMessage(NotificationCompat.MessagingStyle.Message(
88+
it.text,
89+
it.timestampMs,
90+
toAndroidPerson(it.person),
91+
))
92+
} }
93+
setStyle(style)
94+
}
95+
number?.let { setNumber(it.toInt()) }
6396
smallIconResourceName?.let { setSmallIcon(context.resources.getIdentifier(
6497
it, "drawable", context.packageName)) }
6598
}.build()
6699
NotificationManagerCompat.from(context).notify(tag, id.toInt(), notification)
67100
}
101+
102+
override fun getActiveNotificationMessagingStyleByTag(tag: String): MessagingStyle? {
103+
val activeNotification = NotificationManagerCompat.from(context)
104+
.activeNotifications
105+
.find { it.tag == tag }
106+
activeNotification?.notification?.let { notification ->
107+
NotificationCompat.MessagingStyle
108+
.extractMessagingStyleFromNotification(notification)
109+
?.let { style ->
110+
return MessagingStyle(
111+
toPigeonPerson(style.user),
112+
style.conversationTitle!!.toString(),
113+
style.messages.map { MessagingStyleMessage(
114+
it.text!!.toString(),
115+
it.timestamp,
116+
toPigeonPerson(it.person!!)
117+
) },
118+
style.isGroupConversation,
119+
)
120+
}
121+
}
122+
return null
123+
}
68124
}
69125

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

0 commit comments

Comments
 (0)