Skip to content

Commit

Permalink
notif: Add messaging-style notifications support to Pigeon bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
rajveermalviya committed Jun 3, 2024
1 parent 06b84ea commit a646972
Show file tree
Hide file tree
Showing 6 changed files with 438 additions and 8 deletions.
151 changes: 147 additions & 4 deletions android/app/src/main/kotlin/com/zulip/flutter/Notifications.g.kt
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,102 @@ data class InboxStyle (
)
}
}

/**
* Corresponds to `androidx.core.app.Person`
*
* See: https://developer.android.com/reference/androidx/core/app/Person
*
* Generated class from Pigeon that represents data sent in messages.
*/
data class Person (
val iconData: ByteArray? = null,
val key: String,
val name: String

) {
companion object {
@Suppress("LocalVariableName")
fun fromList(__pigeon_list: List<Any?>): Person {
val iconData = __pigeon_list[0] as ByteArray?
val key = __pigeon_list[1] as String
val name = __pigeon_list[2] as String
return Person(iconData, key, name)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
iconData,
key,
name,
)
}
}

/**
* Corresponds to `androidx.core.app.NotificationCompat.MessagingStyle.Message`
*
* See: https://developer.android.com/reference/androidx/core/app/NotificationCompat.MessagingStyle.Message
*
* Generated class from Pigeon that represents data sent in messages.
*/
data class MessagingStyleMessage (
val text: String,
val timestampMs: Long,
val person: Person

) {
companion object {
@Suppress("LocalVariableName")
fun fromList(__pigeon_list: List<Any?>): MessagingStyleMessage {
val text = __pigeon_list[0] as String
val timestampMs = __pigeon_list[1].let { num -> if (num is Int) num.toLong() else num as Long }
val person = __pigeon_list[2] as Person
return MessagingStyleMessage(text, timestampMs, person)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
text,
timestampMs,
person,
)
}
}

/**
* Corresponds to `androidx.core.app.NotificationCompat.MessagingStyle`
*
* See: https://developer.android.com/reference/androidx/core/app/NotificationCompat.MessagingStyle
*
* Generated class from Pigeon that represents data sent in messages.
*/
data class MessagingStyle (
val user: Person,
val conversationTitle: String? = null,
val isGroupConversation: Boolean,
val messages: List<MessagingStyleMessage?>? = null

) {
companion object {
@Suppress("LocalVariableName")
fun fromList(__pigeon_list: List<Any?>): MessagingStyle {
val user = __pigeon_list[0] as Person
val conversationTitle = __pigeon_list[1] as String?
val isGroupConversation = __pigeon_list[2] as Boolean
val messages = __pigeon_list[3] as List<MessagingStyleMessage?>?
return MessagingStyle(user, conversationTitle, isGroupConversation, messages)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
user,
conversationTitle,
isGroupConversation,
messages,
)
}
}
private object AndroidNotificationHostApiCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
Expand All @@ -116,10 +212,25 @@ private object AndroidNotificationHostApiCodec : StandardMessageCodec() {
}
}
129.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MessagingStyle.fromList(it)
}
}
130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MessagingStyleMessage.fromList(it)
}
}
131.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
PendingIntent.fromList(it)
}
}
132.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
Person.fromList(it)
}
}
else -> super.readValueOfType(type, buffer)
}
}
Expand All @@ -129,10 +240,22 @@ private object AndroidNotificationHostApiCodec : StandardMessageCodec() {
stream.write(128)
writeValue(stream, value.toList())
}
is PendingIntent -> {
is MessagingStyle -> {
stream.write(129)
writeValue(stream, value.toList())
}
is MessagingStyleMessage -> {
stream.write(130)
writeValue(stream, value.toList())
}
is PendingIntent -> {
stream.write(131)
writeValue(stream, value.toList())
}
is Person -> {
stream.write(132)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
Expand All @@ -159,7 +282,8 @@ interface AndroidNotificationHostApi {
* https://developer.android.com/reference/kotlin/android/app/NotificationManager.html#notify
* https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder
*/
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?)
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?)
fun getActiveNotificationMessagingStyleByTag(tag: String): MessagingStyle?

companion object {
/** The codec used by AndroidNotificationHostApi. */
Expand All @@ -186,9 +310,11 @@ interface AndroidNotificationHostApi {
val groupKeyArg = args[9] as String?
val isGroupSummaryArg = args[10] as Boolean?
val inboxStyleArg = args[11] as InboxStyle?
val autoCancelArg = args[12] as Boolean?
val messagingStyleArg = args[12] as MessagingStyle?
val numberArg = args[13].let { num -> if (num is Int) num.toLong() else num as Long? }
val autoCancelArg = args[14] as Boolean?
val wrapped: List<Any?> = try {
api.notify(tagArg, idArg, channelIdArg, colorArg, contentIntentArg, contentTextArg, contentTitleArg, extrasArg, smallIconResourceNameArg, groupKeyArg, isGroupSummaryArg, inboxStyleArg, autoCancelArg)
api.notify(tagArg, idArg, channelIdArg, colorArg, contentIntentArg, contentTextArg, contentTitleArg, extrasArg, smallIconResourceNameArg, groupKeyArg, isGroupSummaryArg, inboxStyleArg, messagingStyleArg, numberArg, autoCancelArg)
listOf<Any?>(null)
} catch (exception: Throwable) {
wrapError(exception)
Expand All @@ -199,6 +325,23 @@ interface AndroidNotificationHostApi {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.zulip.AndroidNotificationHostApi.getActiveNotificationMessagingStyleByTag$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val tagArg = args[0] as String
val wrapped: List<Any?> = try {
listOf<Any?>(api.getActiveNotificationMessagingStyleByTag(tagArg))
} catch (exception: Throwable) {
wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
57 changes: 57 additions & 0 deletions android/app/src/main/kotlin/com/zulip/flutter/ZulipPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,28 @@ import android.util.Log
import androidx.annotation.Keep
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.graphics.drawable.IconCompat
import io.flutter.embedding.engine.plugins.FlutterPlugin

private const val TAG = "ZulipPlugin"

fun toAndroidPerson(person: Person): androidx.core.app.Person {
return androidx.core.app.Person.Builder().apply {
person.iconData?.let { setIcon(IconCompat.createWithData(it, 0, it.size)) }
setKey(person.key)
setName(person.name)
}.build()
}

@SuppressLint("RestrictedApi")
fun toPigeonPerson(person: androidx.core.app.Person): Person {
return Person(
null, // TODO: Serialize icon maybe using content URI, mData is not exposed.
person.key!!,
person.name!!.toString(),
)
}

private class AndroidNotificationHost(val context: Context)
: AndroidNotificationHostApi {
@SuppressLint(
Expand All @@ -33,6 +51,8 @@ private class AndroidNotificationHost(val context: Context)
groupKey: String?,
isGroupSummary: Boolean?,
inboxStyle: InboxStyle?,
messagingStyle: MessagingStyle?,
number: Long?,
autoCancel: Boolean?
) {
val notification = NotificationCompat.Builder(context, channelId).apply {
Expand Down Expand Up @@ -61,10 +81,47 @@ private class AndroidNotificationHost(val context: Context)
NotificationCompat.InboxStyle()
.setSummaryText(it.summaryText)
) }
messagingStyle?.let { messagingStyle ->
val style = NotificationCompat.MessagingStyle(toAndroidPerson(messagingStyle.user))
.setConversationTitle(messagingStyle.conversationTitle)
.setGroupConversation(messagingStyle.isGroupConversation)
messagingStyle.messages?.forEach { it?.let {
style.addMessage(NotificationCompat.MessagingStyle.Message(
it.text,
it.timestampMs,
toAndroidPerson(it.person),
))
} }
setStyle(style)
}
number?.let { setNumber(it.toInt()) }
autoCancel?.let { setAutoCancel(it) }
}.build()
NotificationManagerCompat.from(context).notify(tag, id.toInt(), notification)
}

override fun getActiveNotificationMessagingStyleByTag(tag: String): MessagingStyle? {
val activeNotification = NotificationManagerCompat.from(context)
.activeNotifications
.find { it.tag == tag }
activeNotification?.notification?.let { notification ->
NotificationCompat.MessagingStyle
.extractMessagingStyleFromNotification(notification)
?.let { style ->
return MessagingStyle(
toPigeonPerson(style.user),
style.conversationTitle!!.toString(),
style.isGroupConversation,
style.messages.map { MessagingStyleMessage(
it.text!!.toString(),
it.timestamp,
toPigeonPerson(it.person!!)
) }
)
}
}
return null
}
}

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

0 comments on commit a646972

Please sign in to comment.