diff --git a/.idea/modules.xml b/.idea/modules.xml
index 9ff1386a7f..b09507c4ac 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -3,6 +3,7 @@
+
-
+
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/zulip/flutter/Notifications.g.kt b/android/app/src/main/kotlin/com/zulip/flutter/Notifications.g.kt
index f86861e909..d896f319d3 100644
--- a/android/app/src/main/kotlin/com/zulip/flutter/Notifications.g.kt
+++ b/android/app/src/main/kotlin/com/zulip/flutter/Notifications.g.kt
@@ -256,6 +256,65 @@ data class MessagingStyle (
)
}
}
+
+/**
+ * Corresponds to `android.app.Notification`.
+ *
+ * See: https://developer.android.com/reference/kotlin/android/app/Notification
+ *
+ * Generated class from Pigeon that represents data sent in messages.
+ */
+data class Notification (
+ val group: String? = null,
+ val extras: Map
+
+) {
+ companion object {
+ @Suppress("LocalVariableName")
+ fun fromList(__pigeon_list: List): Notification {
+ val group = __pigeon_list[0] as String?
+ val extras = __pigeon_list[1] as Map
+ return Notification(group, extras)
+ }
+ }
+ fun toList(): List {
+ return listOf(
+ group,
+ extras,
+ )
+ }
+}
+
+/**
+ * Corresponds to `android.service.notification.StatusBarNotification`.
+ *
+ * See: https://developer.android.com/reference/kotlin/android/service/notification/StatusBarNotification
+ *
+ * Generated class from Pigeon that represents data sent in messages.
+ */
+data class StatusBarNotification (
+ val id: Long,
+ val notification: Notification,
+ val tag: String? = null
+
+) {
+ companion object {
+ @Suppress("LocalVariableName")
+ fun fromList(__pigeon_list: List): StatusBarNotification {
+ val id = __pigeon_list[0].let { num -> if (num is Int) num.toLong() else num as Long }
+ val notification = __pigeon_list[1] as Notification
+ val tag = __pigeon_list[2] as String?
+ return StatusBarNotification(id, notification, tag)
+ }
+ }
+ fun toList(): List {
+ return listOf(
+ id,
+ notification,
+ tag,
+ )
+ }
+}
private object NotificationsPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
@@ -289,6 +348,16 @@ private object NotificationsPigeonCodec : StandardMessageCodec() {
MessagingStyle.fromList(it)
}
}
+ 135.toByte() -> {
+ return (readValue(buffer) as? List)?.let {
+ Notification.fromList(it)
+ }
+ }
+ 136.toByte() -> {
+ return (readValue(buffer) as? List)?.let {
+ StatusBarNotification.fromList(it)
+ }
+ }
else -> super.readValueOfType(type, buffer)
}
}
@@ -318,6 +387,14 @@ private object NotificationsPigeonCodec : StandardMessageCodec() {
stream.write(134)
writeValue(stream, value.toList())
}
+ is Notification -> {
+ stream.write(135)
+ writeValue(stream, value.toList())
+ }
+ is StatusBarNotification -> {
+ stream.write(136)
+ writeValue(stream, value.toList())
+ }
else -> super.writeValue(stream, value)
}
}
@@ -351,6 +428,7 @@ interface AndroidNotificationHostApi {
* https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder
*/
fun notify(tag: String?, id: Long, autoCancel: Boolean?, channelId: String, color: Long?, contentIntent: PendingIntent?, contentText: String?, contentTitle: String?, extras: Map?, groupKey: String?, inboxStyle: InboxStyle?, isGroupSummary: Boolean?, messagingStyle: MessagingStyle?, number: Long?, smallIconResourceName: String?)
+ fun getActiveNotifications(): List
/**
* Wraps `androidx.core.app.NotificationManagerCompat.getActiveNotifications`,
* combined with `androidx.core.app.NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification`.
@@ -365,6 +443,12 @@ interface AndroidNotificationHostApi {
* https://developer.android.com/reference/kotlin/androidx/core/app/NotificationCompat.MessagingStyle#extractMessagingStyleFromNotification(android.app.Notification)
*/
fun getActiveNotificationMessagingStyleByTag(tag: String): MessagingStyle?
+ /**
+ * Corresponds to `android.app.NotificationManager.cancel`.
+ *
+ * See: https://developer.android.com/reference/kotlin/android/app/NotificationManager.html#cancel
+ */
+ fun cancel(tag: String?, id: Long)
companion object {
/** The codec used by AndroidNotificationHostApi. */
@@ -425,6 +509,21 @@ interface AndroidNotificationHostApi {
channel.setMessageHandler(null)
}
}
+ run {
+ val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.zulip.AndroidNotificationHostApi.getActiveNotifications$separatedMessageChannelSuffix", codec)
+ if (api != null) {
+ channel.setMessageHandler { _, reply ->
+ val wrapped: List = try {
+ listOf(api.getActiveNotifications())
+ } catch (exception: Throwable) {
+ wrapError(exception)
+ }
+ reply.reply(wrapped)
+ }
+ } else {
+ channel.setMessageHandler(null)
+ }
+ }
run {
val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.zulip.AndroidNotificationHostApi.getActiveNotificationMessagingStyleByTag$separatedMessageChannelSuffix", codec)
if (api != null) {
@@ -442,6 +541,25 @@ interface AndroidNotificationHostApi {
channel.setMessageHandler(null)
}
}
+ run {
+ val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.zulip.AndroidNotificationHostApi.cancel$separatedMessageChannelSuffix", codec)
+ if (api != null) {
+ channel.setMessageHandler { message, reply ->
+ val args = message as List
+ val tagArg = args[0] as String?
+ val idArg = args[1].let { num -> if (num is Int) num.toLong() else num as Long }
+ val wrapped: List = try {
+ api.cancel(tagArg, idArg)
+ listOf(null)
+ } catch (exception: Throwable) {
+ wrapError(exception)
+ }
+ reply.reply(wrapped)
+ }
+ } else {
+ channel.setMessageHandler(null)
+ }
+ }
}
}
}
diff --git a/android/app/src/main/kotlin/com/zulip/flutter/ZulipPlugin.kt b/android/app/src/main/kotlin/com/zulip/flutter/ZulipPlugin.kt
index c357f70043..3314ef2695 100644
--- a/android/app/src/main/kotlin/com/zulip/flutter/ZulipPlugin.kt
+++ b/android/app/src/main/kotlin/com/zulip/flutter/ZulipPlugin.kt
@@ -1,6 +1,7 @@
package com.zulip.flutter
import android.annotation.SuppressLint
+import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.os.Bundle
@@ -14,6 +15,13 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin
private const val TAG = "ZulipPlugin"
+private fun Bundle.stringsMap(): Map {
+ return keySet().associateWith {
+ try { get(it) as CharSequence? }
+ catch (e: Throwable) { null }
+ }
+}
+
fun toAndroidPerson(person: Person): androidx.core.app.Person {
return androidx.core.app.Person.Builder().apply {
person.iconBitmap?.let { setIcon(IconCompat.createWithData(it, 0, it.size)) }
@@ -121,6 +129,20 @@ private class AndroidNotificationHost(val context: Context)
NotificationManagerCompat.from(context).notify(tag, id.toInt(), notification)
}
+ override fun getActiveNotifications(): List {
+ val notificationManager = context.getSystemService(NotificationManager::class.java)!!
+ return notificationManager.activeNotifications.map {
+ StatusBarNotification(
+ id = it.id.toLong(),
+ tag = it.tag,
+ notification = Notification(
+ group = it.notification.group,
+ extras = it.notification.extras.stringsMap() as Map,
+ )
+ )
+ }
+ }
+
override fun getActiveNotificationMessagingStyleByTag(tag: String): MessagingStyle? {
val activeNotification = NotificationManagerCompat.from(context)
.activeNotifications
@@ -143,6 +165,11 @@ private class AndroidNotificationHost(val context: Context)
}
return null
}
+
+ override fun cancel(tag: String?, id: Long) {
+ val notificationManager = context.getSystemService(NotificationManager::class.java)!!
+ notificationManager.cancel(tag, id.toInt())
+ }
}
/** A Flutter plugin for the Zulip app's ad-hoc needs. */
diff --git a/lib/host/android_notifications.g.dart b/lib/host/android_notifications.g.dart
index 413376731e..ce1068fae2 100644
--- a/lib/host/android_notifications.g.dart
+++ b/lib/host/android_notifications.g.dart
@@ -240,6 +240,69 @@ class MessagingStyle {
}
}
+/// Corresponds to `android.app.Notification`.
+///
+/// See: https://developer.android.com/reference/kotlin/android/app/Notification
+class Notification {
+ Notification({
+ this.group,
+ required this.extras,
+ });
+
+ String? group;
+
+ Map extras;
+
+ Object encode() {
+ return