Skip to content

Conversation

@VelikovPetar
Copy link
Contributor

@VelikovPetar VelikovPetar commented Jan 8, 2026

🎯 Goal

Introduces a new NotificationIdFactory which can be used by customers to override the default notification ID generation logic. This allows customers to adjust the default grouping logic for channel notifications. For example, this can be used to display system messages outside of the standard message grouping.

Example usage:

val notificationIdFactory = NotificationIdFactory { notification ->
    if (notification is ChatNotification.MessageNew && notification.message.isSystem()) {
         // Use a custom ID for system messages to avoid default grouping by channel
         return@NotificationIdFactory "system:${notification.message.id}".hashCode()
    }
    return@NotificationIdFactory null
}

🛠 Implementation details

  • Introduce new NotificationIdFactory - applied via NotificationHandlerFactory.createNotificationHandler
  • Re-work the auto-dismiss notification logic to consider custom notification IDs

🎨 UI Changes

Example
example.mp4

🧪 Testing

  1. Apply the provided patch
  2. Send regular and system messages
  3. The system messages should not be grouped together with the regular ones
Example for system messages
Index: stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ChatHelper.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ChatHelper.kt b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ChatHelper.kt
--- a/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ChatHelper.kt	(revision 95833740296748a7acf035876d1434e1526bba84)
+++ b/stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ChatHelper.kt	(date 1767872003407)
@@ -21,8 +21,11 @@
 import io.getstream.android.push.firebase.FirebasePushDeviceGenerator
 import io.getstream.chat.android.client.ChatClient
 import io.getstream.chat.android.client.logger.ChatLogLevel
+import io.getstream.chat.android.client.notifications.handler.ChatNotification
 import io.getstream.chat.android.client.notifications.handler.NotificationConfig
 import io.getstream.chat.android.client.notifications.handler.NotificationHandlerFactory
+import io.getstream.chat.android.client.notifications.handler.NotificationIdFactory
+import io.getstream.chat.android.client.utils.message.isSystem
 import io.getstream.chat.android.compose.sample.data.UserCredentials
 import io.getstream.chat.android.compose.sample.ui.StartupActivity
 import io.getstream.chat.android.models.Channel
@@ -59,6 +62,15 @@
             ),
             autoTranslationEnabled = ChatApp.autoTranslationEnabled,
         )
+
+        val notificationIdFactory = NotificationIdFactory { notification ->
+            if (notification is ChatNotification.MessageNew && notification.message.isSystem()) {
+                // Use a custom ID for system messages to avoid default grouping by channel
+                return@NotificationIdFactory "system:${notification.message.id}".hashCode()
+            }
+            return@NotificationIdFactory null
+        }
+
         val notificationHandler = NotificationHandlerFactory.createNotificationHandler(
             context = context,
             notificationConfig = notificationConfig,
@@ -80,6 +92,7 @@
                 // Return false to let the SDK handle the push message and show a notification
                 false
             },
+            notificationIdFactory = notificationIdFactory,
         )
 
         val offlinePlugin = StreamOfflinePluginFactory(context)

Summary by CodeRabbit

  • New Features

    • Added NotificationIdFactory to allow custom notification IDs.
    • Added per-channel notification tracking for finer notification management.
  • Bug Fixes

    • Improved dismissal logic to correctly cancel and clear channel-specific and global notifications.
  • Tests

    • Expanded tests covering default and custom notification ID generation and multi-channel dismissal behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@VelikovPetar VelikovPetar changed the title Feature/and 1006 notification id customisation Introduce NotificationIdFactory for customizing the default notification IDs Jan 8, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 8, 2026

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.25 MB 5.25 MB 0.00 MB 🟢
stream-chat-android-offline 5.48 MB 5.48 MB 0.00 MB 🟢
stream-chat-android-ui-components 10.60 MB 10.60 MB 0.00 MB 🟢
stream-chat-android-compose 12.81 MB 12.81 MB 0.00 MB 🟢

@VelikovPetar VelikovPetar marked this pull request as ready for review January 8, 2026 12:03
@VelikovPetar VelikovPetar requested a review from a team as a code owner January 8, 2026 12:03
@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

Walkthrough

Adds a new public functional interface NotificationIdFactory and wires it through notification creation and dismissal flows so callers can supply custom notification IDs; falls back to existing ID generation when the factory is null or returns null. Also introduces per-channel tracking for notifications and updates tests.

Changes

Cohort / File(s) Summary
New Public API
stream-chat-android-client/src/main/java/.../NotificationIdFactory.kt, stream-chat-android-client/api/stream-chat-android-client.api
Adds NotificationIdFactory functional interface with getNotificationId(ChatNotification): Int? and exposes it in the module API.
Notification handler wiring
stream-chat-android-client/src/main/java/.../NotificationHandlerFactory.kt
Adds optional notificationIdFactory: NotificationIdFactory? = null parameter to createNotificationHandler(...) and propagates it to handlers.
Chat-level notification handling
stream-chat-android-client/src/main/java/.../ChatNotificationHandler.kt
Constructor gains notificationIdFactory: NotificationIdFactory?; replaces inline ID generation with createNotificationId(notification) that consults the factory and falls back to defaults.
Messaging-style creation & restoration
stream-chat-android-client/src/main/java/.../MessagingStyleNotificationFactory.kt
Accepts optional notificationIdFactory; centralizes ID generation via createNotificationId/createDefaultNotificationId and uses it across creation/restoration paths.
Messaging-style lifecycle & tracking
stream-chat-android-client/src/main/java/.../MessagingStyleNotificationHandler.kt
Adds notificationIdFactory to constructor; replaces global-only tracking with per-channel SharedPreferences tracking keys, adds helpers for tracking/dismissing per-channel and clearing all tracked IDs; updates dismissal flows.
Tests
stream-chat-android-client/src/test/java/.../ChatNotificationHandlerTest.kt, .../MessagingStyleNotificationFactoryTest.kt, .../MessagingStyleNotificationHandlerTest.kt
New/updated tests verifying default ID behavior, factory override behavior, and per-channel/global dismissal and storage cleanup.
Changelog
CHANGELOG.md
Documents the new NotificationIdFactory entry.

Sequence Diagram(s)

sequenceDiagram
  actor App
  participant NotificationHandlerFactory
  participant MessagingStyleNotificationHandler
  participant MessagingStyleNotificationFactory
  participant NotificationManager

  App->>NotificationHandlerFactory: createNotificationHandler(..., notificationIdFactory)
  NotificationHandlerFactory->>MessagingStyleNotificationHandler: instantiate(..., notificationIdFactory)
  MessagingStyleNotificationHandler->>MessagingStyleNotificationFactory: buildNotification(push, notificationIdFactory)
  MessagingStyleNotificationFactory->>MessagingStyleNotificationFactory: createNotificationId(chatNotification)
  alt factory returns non-null
    MessagingStyleNotificationFactory->>NotificationManager: notify(notificationId from factory, notification)
  else factory null or returns null
    MessagingStyleNotificationFactory->>MessagingStyleNotificationFactory: createDefaultNotificationId(...)
    MessagingStyleNotificationFactory->>NotificationManager: notify(generatedId, notification)
  end
  MessagingStyleNotificationHandler->>MessagingStyleNotificationHandler: trackChannelNotificationId(channel, notificationId)
Loading
sequenceDiagram
  actor System
  participant MessagingStyleNotificationHandler
  participant NotificationManager
  participant Storage

  System->>MessagingStyleNotificationHandler: dismissChannelNotifications(channel)
  MessagingStyleNotificationHandler->>Storage: getChannelNotificationIds(channel)
  MessagingStyleNotificationHandler->>NotificationManager: cancel(notificationId) for each id
  MessagingStyleNotificationHandler->>Storage: remove channel key
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • gpunto

Poem

🐰 I hopped in code to change the bell,
Gave IDs a choice — custom or fallback well.
Per-channel crumbs in SharedPrefs I stashed,
So dismissals clean up, nothing left abashed.
Hop, notify, and nap — this rabbit's work is done 🥕🔔

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.98% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately and concisely summarizes the main change: introducing NotificationIdFactory for customizing notification IDs.
Description check ✅ Passed The PR description includes goal, implementation details, UI changes with video, testing instructions with patch, and follows the template structure well.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • UTF-8: Entity not found: Issue - Could not find referenced Issue.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/NotificationIdFactory.kt (1)

19-49: NotificationIdFactory API and docs look good; consider minor KDoc clarifications

The fun interface shape and nullable return to trigger default ID generation align well with the intended customization use case, and the example is clear and practical.

As a small improvement, you might extend the KDoc to clarify:

  • On which threads getNotificationId may be invoked (main/UI vs background), and
  • Any expectations around ID stability/uniqueness (e.g., whether callers should assume the SDK may reuse IDs to group notifications by channel, and whether negative/zero values are acceptable).

This would help integrators reason about concurrency and grouping behavior without digging into the implementation.

stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandlerTest.kt (1)

199-268: Comprehensive test coverage for notification ID factory behavior.

The three test methods effectively cover:

  1. Null factory → default ID generation
  2. Factory returning null → fallback to default
  3. Custom factory with non-null ID → custom ID is used

Line 267 correctly verifies that the custom notification ID is used for the individual notification. The summary notification uses a separate ID derivation (channel hash), which is intentional design.

Optional: Consider testing edge cases

For additional robustness, consider testing:

  • Factory returning negative IDs
  • Factory throwing exceptions (if resilience is desired)

These are optional enhancements for more defensive testing.

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandler.kt (1)

153-163: Clean implementation of factory-based ID generation with appropriate defaults.

The implementation correctly:

  • Uses the factory when provided and non-null
  • Falls back to default generation when factory is null or returns null
  • Uses System.nanoTime().toInt() for MessageNew/MessageUpdated (pre-M Android) to ensure each notification gets a unique ID
  • Uses hash-based IDs for ReactionNew and NotificationReminderDue

Note: This differs from MessagingStyleNotificationFactory which uses hash-based IDs for MessageNew (API 23+). This difference is intentional—pre-M Android lacks MessagingStyle grouping capabilities, so time-based IDs prevent unwanted notification replacement.

Optional: Consider extracting common structure

The createNotificationId and createDefaultNotificationId methods share identical structure with MessagingStyleNotificationFactory.kt (lines 77-89). While the default implementations differ intentionally, the factory delegation pattern could potentially be extracted to a shared helper to reduce structural duplication.

However, given the different default strategies (time-based vs. hash-based) and that these classes serve different Android API levels, the current approach is acceptable.

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationHandler.kt (3)

167-168: Consider consolidating channel notification tracking with global tracking.

The per-channel tracking logic (lines 167-183) duplicates patterns from the global tracking methods (getShownNotifications, trackNotificationId, removeNotificationId). Consider extracting a shared helper that can work with different key prefixes to reduce duplication.

♻️ Potential refactor to reduce duplication
private fun trackNotificationIdWithKey(key: String, notificationId: Int) {
    sharedPreferences.edit {
        putStringSet(
            key,
            (getNotificationIdsForKey(key) + notificationId).map(Int::toString).toSet()
        )
    }
}

private fun getNotificationIdsForKey(key: String): Set<Int> =
    sharedPreferences.getStringSet(key, null).orEmpty().mapNotNull { it.toIntOrNull() }.toSet()

private fun trackChannelNotificationId(channelType: String, channelId: String, notificationId: Int) {
    trackNotificationIdWithKey(getChannelNotificationsKey(channelType, channelId), notificationId)
}

private fun trackNotificationId(notificationId: Int) {
    trackNotificationIdWithKey(KEY_NOTIFICATIONS_SHOWN, notificationId)
}

Also applies to: 170-183


197-208: Potential performance concern with iteration over all SharedPreferences keys.

The method iterates over all SharedPreferences keys and filters by prefix. If there are many unrelated keys in SharedPreferences, this could be inefficient. However, given that this is only called during dismissAllNotifications, which is likely infrequent, the performance impact is probably acceptable.


167-216: Consider SharedPreferences cleanup strategy for abandoned channels.

Per-channel tracking creates a new SharedPreferences entry for each channel (e.g., KEY_CHANNEL_NOTIFICATIONS_messaging:channel123). Over time, if channels are deleted or abandoned, these entries could accumulate. While dismissChannelNotifications clears the entry, it's only called when explicitly dismissing notifications for that channel. Consider documenting the expected cleanup lifecycle or adding periodic cleanup logic for stale entries.

Would you like me to generate a cleanup strategy or documentation for managing per-channel SharedPreferences entries?

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8e81c97 and 56aeba2.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • stream-chat-android-client/api/stream-chat-android-client.api
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandler.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactory.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationHandler.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/NotificationHandlerFactory.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/NotificationIdFactory.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandlerTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactoryTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationHandlerTest.kt
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{kt,kts}

📄 CodeRabbit inference engine (AGENTS.md)

Format and apply Kotlin style with Spotless (4 spaces, no wildcard imports, licence headers)

Files:

  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/NotificationIdFactory.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandler.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationHandlerTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandlerTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactoryTest.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactory.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationHandler.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/NotificationHandlerFactory.kt
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/*.kt: Use @OptIn annotations explicitly; avoid suppressions unless documented
Document public APIs with KDoc, including thread expectations and state notes

Files:

  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/NotificationIdFactory.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandler.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationHandlerTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandlerTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactoryTest.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactory.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationHandler.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/NotificationHandlerFactory.kt
**/src/test/**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/src/test/**/*.kt: Use backtick test names (for example: fun message list filters muted channels()) for readability
Use deterministic tests with runTest + virtual time for concurrency-sensitive logic (uploads, sync, message state)
Keep helper extensions private/internal in test files

Files:

  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationHandlerTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandlerTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactoryTest.kt
🧠 Learnings (4)
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-ui-components/**/*Test.kt : Record Shot baselines when behaviour changes in XML kit UI tests

Applied to files:

  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationHandlerTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandlerTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactoryTest.kt
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-compose/**/*Test.kt : Add Paparazzi snapshots for Compose UI regressions and run `verifyPaparazziDebug`

Applied to files:

  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandlerTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactoryTest.kt
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/src/test/**/*.kt : Use deterministic tests with `runTest` + virtual time for concurrency-sensitive logic (uploads, sync, message state)

Applied to files:

  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandlerTest.kt
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/src/test/**/*.kt : Use backtick test names (for example: `fun `message list filters muted channels`()`) for readability

Applied to files:

  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandlerTest.kt
🧬 Code graph analysis (5)
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandler.kt (1)
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactory.kt (2)
  • createNotificationId (77-78)
  • createDefaultNotificationId (80-89)
stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationHandlerTest.kt (1)
stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt (1)
  • randomString (94-98)
stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactoryTest.kt (1)
stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt (2)
  • randomChannel (423-483)
  • randomMessage (302-399)
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactory.kt (1)
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandler.kt (2)
  • createDefaultNotificationId (156-163)
  • createNotificationId (153-154)
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationHandler.kt (1)
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandler.kt (1)
  • removeNotificationId (323-332)
🔇 Additional comments (24)
CHANGELOG.md (1)

19-19: Changelog entry is clear and consistent with existing style

The wording, placement under ✅ Added, and PR link formatting all match the rest of the changelog; no changes needed.

stream-chat-android-client/api/stream-chat-android-client.api (1)

2931-2947: NotificationIdFactory wiring into public API looks consistent and backward compatible

The additional createNotificationHandler overload that takes NotificationIdFactory and the corresponding NotificationIdFactory interface entry correctly expose the new customization point without altering existing signatures. This should preserve compatibility while enabling the new behavior. No issues from an API-surface perspective.

stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandlerTest.kt (2)

44-44: LGTM!

The eq import is properly added to support the verification on line 267 using eq(customNotificationId).


107-107: Correctly updated to reflect the new constructor signature.

The test setup properly passes notificationIdFactory = null to verify default behavior.

stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationHandlerTest.kt (3)

116-116: Correctly updated to reflect the new constructor signature.

The test setup properly passes notificationIdFactory = null to verify default behavior.


141-158: Well-structured test for multi-notification dismissal.

The test now properly verifies that:

  • Multiple channel notifications (IDs 100, 200) are tracked per channel
  • All tracked notifications are cancelled
  • Channel storage is cleaned up via remove(channelKey)

This aligns with the updated implementation supporting custom notification IDs.


161-193: Comprehensive test for global and channel notification dismissal.

The test effectively covers:

  • Global notification dismissal (ID 1)
  • Multiple channel notification dismissal (IDs 10, 20)
  • Global storage cleared with putStringSet(globalKey, emptySet())
  • Channel storage cleared with remove(channelKey1) and remove(channelKey2)

This provides good coverage of the complete dismissal flow.

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/NotificationHandlerFactory.kt (1)

61-67: Excellent integration of the notification ID factory.

The changes properly:

  • Document the new parameter with clear fallback behavior explanation
  • Provide a sensible default (null) for backward compatibility
  • Propagate the factory to both handler implementations (MessagingStyleNotificationHandler for API 23+ and ChatNotificationHandler for older versions)

The @see NotificationIdFactory reference helps developers discover the new customization option.

Also applies to: 86-110

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/ChatNotificationHandler.kt (2)

58-58: LGTM!

The constructor correctly accepts the optional notificationIdFactory parameter.


107-107: Excellent centralization of notification ID generation.

All notification creation sites now consistently use createNotificationId(notification), which provides a single point of control for ID generation with proper factory support.

Also applies to: 121-121, 133-133, 144-144

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactory.kt (3)

49-50: Clear documentation for the notification ID factory parameter.

The KDoc properly explains the fallback behavior when the factory is null or returns null.

Also applies to: 66-66


77-89: Solid implementation of factory-based ID generation with appropriate defaults.

The implementation correctly:

  • Delegates to the factory when provided and non-null
  • Falls back to default hash-based generation
  • Uses channel.type:channel.id hash for MessageNew, enabling proper grouping for MessagingStyle (API 23+)
  • Applies context-specific hashing for other notification types

The hash-based approach for MessageNew differs intentionally from ChatNotificationHandler (which uses System.nanoTime() for pre-M Android). This design choice supports MessagingStyle's channel-based grouping on newer Android versions.


108-108: Appropriate signature change for restoreMessagingStyle.

Changing from restoreMessagingStyle(channel: Channel) to restoreMessagingStyle(notification: ChatNotification) makes sense because:

  • The notification ID is now computed from the complete ChatNotification context (not just the channel)
  • Line 155 correctly uses createNotificationId(notification) to locate the matching active notification

This ensures that custom notification IDs are properly used for MessagingStyle restoration.

Also applies to: 119-119, 153-157

stream-chat-android-client/src/test/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationFactoryTest.kt (4)

74-74: LGTM! Explicit null initialization supports default behavior testing.

Setting notificationIdFactory = null explicitly in the test setup makes it clear that the default behavior is being tested in most test cases.


215-228: LGTM! Clear test for default behavior when factory is null.

The test properly validates that the default ID generation logic is used when no custom factory is provided.


230-256: LGTM! Proper coverage of fallback behavior.

This test correctly validates the fallback to default ID generation when a factory is provided but returns null, ensuring robustness of the factory pattern.


258-284: LGTM! Custom factory override test is well-structured.

The test properly validates that custom notification IDs are used when a factory provides non-null values, completing the coverage of all three factory scenarios.

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/notifications/handler/MessagingStyleNotificationHandler.kt (7)

57-57: LGTM! Parameter addition enables factory customization.

The optional notificationIdFactory parameter is correctly added to support custom notification ID generation.


83-83: LGTM! Factory properly wired through to notification factory.


107-111: LGTM! Channel-specific dismissal is correctly implemented.

The logic properly dismisses and clears tracked notifications for a specific channel.


113-122: LGTM! Comprehensive cleanup in dismissAllNotifications.

The method correctly dismisses both globally tracked notifications and all channel-specific tracked notifications, ensuring complete cleanup.


144-148: LGTM! Renamed method clarifies intent.

Renaming from addNotificationId to trackNotificationId better reflects the purpose of the method.


221-221: LGTM! Constant properly defined for per-channel tracking keys.


131-136: Dual tracking behavior with custom notification IDs is intended and documented.

The implementation correctly supports custom notificationIdFactory strategies. The NotificationIdFactory documentation explicitly shows the intended use case: returning custom IDs for system messages "to avoid default grouping by channel". Each notification ID is independently tracked via trackChannelNotificationId, which is the designed behavior. Existing tests in ChatNotificationHandlerTest and MessagingStyleNotificationFactoryTest verify custom factory usage. No changes needed.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
CHANGELOG.md (1)

18-20: Tighten wording to specify “notificationId” / “Android notification IDs” (optional).

Current entry is accurate, but “notification IDs” can read a bit generic; consider “notificationId generation” or “Android notification IDs” for clarity/SEO in release notes.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 56aeba2 and 2a91fec.

📒 Files selected for processing (1)
  • CHANGELOG.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: compare-sdk-sizes / Compare SDK sizes
  • GitHub Check: base-android-ci / Run static checks
  • GitHub Check: base-android-ci / Run unit tests
  • GitHub Check: base-android-ci / Build
  • GitHub Check: Detekt
  • GitHub Check: Build / compose apks

@sonarqubecloud
Copy link

@VelikovPetar VelikovPetar merged commit 5569303 into develop Jan 12, 2026
19 of 20 checks passed
@VelikovPetar VelikovPetar deleted the feature/AND-1006_notification_id_customisation branch January 12, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants