diff --git a/native/android/app/src/main/java/app/comm/android/notifications/CommAndroidNotifications.java b/native/android/app/src/main/java/app/comm/android/notifications/CommAndroidNotifications.java
--- a/native/android/app/src/main/java/app/comm/android/notifications/CommAndroidNotifications.java
+++ b/native/android/app/src/main/java/app/comm/android/notifications/CommAndroidNotifications.java
@@ -62,8 +62,9 @@
         continue;
       }
       String notificationThreadID = data.getString("threadID");
-      if (notificationThreadID != null &&
-          notificationThreadID.equals(threadID)) {
+      String notificationGroup = notification.getNotification().getGroup();
+      if (threadID.equals(notificationThreadID) ||
+          threadID.equals(notificationGroup)) {
         notificationManager.cancel(notification.getTag(), notification.getId());
       }
     }
diff --git a/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java b/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java
--- a/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java
+++ b/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java
@@ -152,13 +152,19 @@
         Lifecycle.State.RESUMED;
   }
 
+  private boolean notificationGroupingSupported() {
+    // Comm doesn't support notification grouping for clients running
+    // Android versions older than 23
+    return android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.M;
+  }
+
   private void handleNotificationRescind(RemoteMessage message) {
     String setUnreadStatus = message.getData().get(SET_UNREAD_STATUS_KEY);
+    String threadID = message.getData().get(THREAD_ID_KEY);
     if ("true".equals(setUnreadStatus)) {
       File sqliteFile =
           this.getApplicationContext().getDatabasePath("comm.sqlite");
       if (sqliteFile.exists()) {
-        String threadID = message.getData().get(THREAD_ID_KEY);
         GlobalDBSingleton.scheduleOrRun(() -> {
           ThreadOperations.updateSQLiteUnreadStatus(
               sqliteFile.getPath(), threadID, false);
@@ -170,13 +176,50 @@
       }
     }
     String rescindID = message.getData().get(RESCIND_ID_KEY);
+    boolean groupSummaryPresent = false;
+    boolean threadGroupPresent = false;
+
     for (StatusBarNotification notification :
          notificationManager.getActiveNotifications()) {
       String tag = notification.getTag();
+      boolean isGroupMember =
+          threadID.equals(notification.getNotification().getGroup());
+      boolean isGroupSummary =
+          (notification.getNotification().flags &
+           Notification.FLAG_GROUP_SUMMARY) == Notification.FLAG_GROUP_SUMMARY;
       if (tag != null && tag.equals(rescindID)) {
         notificationManager.cancel(notification.getTag(), notification.getId());
+      } else if (isGroupMember && isGroupSummary) {
+        groupSummaryPresent = true;
+      } else if (isGroupMember) {
+        threadGroupPresent = true;
       }
     }
+
+    if (groupSummaryPresent && !threadGroupPresent) {
+      notificationManager.cancel(threadID, threadID.hashCode());
+    }
+  }
+
+  private void addToThreadGroupAndDisplay(
+      String notificationID,
+      NotificationCompat.Builder notificationBuilder,
+      String threadID) {
+
+    notificationBuilder = notificationBuilder.setGroup(threadID);
+    NotificationCompat.Builder groupSummaryNotificationBuilder =
+        new NotificationCompat.Builder(this.getApplicationContext())
+            .setChannelId(CHANNEL_ID)
+            .setSmallIcon(R.drawable.notif_icon)
+            .setGroup(threadID)
+            .setGroupSummary(true)
+            .setAutoCancel(true)
+            .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN);
+
+    notificationManager.notify(
+        notificationID, notificationID.hashCode(), notificationBuilder.build());
+    notificationManager.notify(
+        threadID, threadID.hashCode(), groupSummaryNotificationBuilder.build());
   }
 
   private void displayNotification(RemoteMessage message) {
@@ -215,7 +258,12 @@
     if (title != null) {
       notificationBuilder = notificationBuilder.setContentTitle(title);
     }
-    notificationManager.notify(id, id.hashCode(), notificationBuilder.build());
+    if (!this.notificationGroupingSupported() || threadID == null) {
+      notificationManager.notify(
+          id, id.hashCode(), notificationBuilder.build());
+      return;
+    }
+    this.addToThreadGroupAndDisplay(id, notificationBuilder, threadID);
   }
 
   private PendingIntent createStartMainActivityAction(RemoteMessage message) {