1818
1919import androidx .annotation .Nullable ;
2020
21- import com .bumptech .glide .Glide ;
22- import com .bumptech .glide .load .resource .bitmap .RoundedCorners ;
23- import com .bumptech .glide .request .RequestOptions ;
2421import com .facebook .react .bridge .ReactApplicationContext ;
2522import com .google .gson .Gson ;
2623
3027import java .util .List ;
3128import java .util .Map ;
3229import java .util .concurrent .ConcurrentHashMap ;
33- import java .util .concurrent .ExecutionException ;
34- import java .util .concurrent .TimeUnit ;
35- import java .util .concurrent .TimeoutException ;
3630
3731import chat .rocket .reactnative .BuildConfig ;
3832import chat .rocket .reactnative .MainActivity ;
@@ -61,7 +55,7 @@ public class CustomPushNotification {
6155
6256 // Instance fields
6357 private final Context mContext ;
64- private Bundle mBundle ;
58+ private volatile Bundle mBundle ;
6559 private final NotificationManager notificationManager ;
6660
6761 public CustomPushNotification (Context context , Bundle bundle ) {
@@ -300,9 +294,6 @@ private void showNotification(Bundle bundle, Ejson ejson, String notId) {
300294 bundle .putString ("senderId" , hasSender ? ejson .sender ._id : "1" );
301295
302296 String avatarUri = ejson != null ? ejson .getAvatarUri () : null ;
303- if (ENABLE_VERBOSE_LOGS ) {
304- Log .d (TAG , "[showNotification] avatarUri=" + (avatarUri != null ? "[present]" : "[null]" ));
305- }
306297 bundle .putString ("avatarUri" , avatarUri );
307298
308299 // Handle special notification types
@@ -379,10 +370,27 @@ private Notification.Builder buildNotification(int notificationId) {
379370 Boolean notificationLoaded = mBundle .getBoolean ("notificationLoaded" , false );
380371 Ejson ejson = safeFromJson (mBundle .getString ("ejson" , "{}" ), Ejson .class );
381372
373+ // Determine the correct title based on notification type
374+ String notificationTitle = title ;
375+ if (ejson != null && ejson .type != null ) {
376+ if ("p" .equals (ejson .type ) || "c" .equals (ejson .type )) {
377+ // For groups/channels, use room name if available, otherwise fall back to title
378+ notificationTitle = (ejson .name != null && !ejson .name .isEmpty ()) ? ejson .name : title ;
379+ } else if ("d" .equals (ejson .type )) {
380+ // For direct messages, use title (sender name from server)
381+ notificationTitle = title ;
382+ } else if ("l" .equals (ejson .type )) {
383+ // For omnichannel, use sender name if available, otherwise fall back to title
384+ notificationTitle = (ejson .sender != null && ejson .sender .name != null && !ejson .sender .name .isEmpty ())
385+ ? ejson .sender .name : title ;
386+ }
387+ }
388+
382389 if (ENABLE_VERBOSE_LOGS ) {
383390 Log .d (TAG , "[buildNotification] notId=" + notId );
384391 Log .d (TAG , "[buildNotification] notificationLoaded=" + notificationLoaded );
385392 Log .d (TAG , "[buildNotification] title=" + (title != null ? "[present]" : "[null]" ));
393+ Log .d (TAG , "[buildNotification] notificationTitle=" + (notificationTitle != null ? "[present]" : "[null]" ));
386394 Log .d (TAG , "[buildNotification] message length=" + (message != null ? message .length () : 0 ));
387395 }
388396
@@ -406,7 +414,7 @@ private Notification.Builder buildNotification(int notificationId) {
406414 }
407415
408416 notification
409- .setContentTitle (title )
417+ .setContentTitle (notificationTitle )
410418 .setContentText (message )
411419 .setContentIntent (pendingIntent )
412420 .setPriority (Notification .PRIORITY_HIGH )
@@ -455,37 +463,7 @@ private void cancelPreviousFallbackNotifications(Ejson ejson) {
455463 }
456464
457465 private Bitmap getAvatar (String uri ) {
458- if (uri == null || uri .isEmpty ()) {
459- if (ENABLE_VERBOSE_LOGS ) {
460- Log .w (TAG , "getAvatar called with null/empty URI" );
461- }
462- return largeIcon ();
463- }
464-
465- if (ENABLE_VERBOSE_LOGS ) {
466- String sanitizedUri = uri ;
467- int queryStart = uri .indexOf ("?" );
468- if (queryStart != -1 ) {
469- sanitizedUri = uri .substring (0 , queryStart ) + "?[auth_params]" ;
470- }
471- Log .d (TAG , "Fetching avatar from: " + sanitizedUri );
472- }
473-
474- try {
475- // Use a 3-second timeout to avoid blocking the FCM service for too long
476- // FCM has a 10-second limit, so we need to fail fast and use fallback icon
477- Bitmap avatar = Glide .with (mContext )
478- .asBitmap ()
479- .apply (RequestOptions .bitmapTransform (new RoundedCorners (10 )))
480- .load (uri )
481- .submit (100 , 100 )
482- .get (3 , TimeUnit .SECONDS );
483-
484- return avatar != null ? avatar : largeIcon ();
485- } catch (final ExecutionException | InterruptedException | TimeoutException e ) {
486- Log .e (TAG , "Failed to fetch avatar: " + e .getMessage (), e );
487- return largeIcon ();
488- }
466+ return NotificationHelper .fetchAvatarBitmap (mContext , uri , largeIcon ());
489467 }
490468
491469 private Bitmap largeIcon () {
@@ -506,7 +484,10 @@ private void notificationIcons(Notification.Builder notification, Bundle bundle)
506484 if (Build .VERSION .SDK_INT < Build .VERSION_CODES .N ) {
507485 String avatarUri = ejson != null ? ejson .getAvatarUri () : null ;
508486 if (avatarUri != null ) {
509- notification .setLargeIcon (getAvatar (avatarUri ));
487+ Bitmap avatar = getAvatar (avatarUri );
488+ if (avatar != null ) {
489+ notification .setLargeIcon (avatar );
490+ }
510491 }
511492 }
512493 }
@@ -517,8 +498,11 @@ private String extractMessage(String message, Ejson ejson) {
517498 }
518499 if (ejson != null && ejson .type != null && !ejson .type .equals ("d" )) {
519500 int pos = message .indexOf (":" );
520- int start = pos == -1 ? 0 : pos + 2 ;
521- return message .substring (start );
501+ if (pos == -1 ) {
502+ return message ;
503+ }
504+ int start = pos + 2 ;
505+ return start <= message .length () ? message .substring (start ) : "" ;
522506 }
523507 return message ;
524508 }
@@ -559,7 +543,23 @@ private void notificationStyle(Notification.Builder notification, int notId, Bun
559543 }
560544
561545 String title = bundle .getString ("title" );
562- messageStyle .setConversationTitle (title );
546+ // Determine the correct conversation title based on notification type
547+ Ejson bundleEjson = safeFromJson (bundle .getString ("ejson" , "{}" ), Ejson .class );
548+ String conversationTitle = title ;
549+ if (bundleEjson != null && bundleEjson .type != null ) {
550+ if ("p" .equals (bundleEjson .type ) || "c" .equals (bundleEjson .type )) {
551+ // For groups/channels, use room name if available, otherwise fall back to title
552+ conversationTitle = (bundleEjson .name != null && !bundleEjson .name .isEmpty ()) ? bundleEjson .name : title ;
553+ } else if ("d" .equals (bundleEjson .type )) {
554+ // For direct messages, use title (sender name from server)
555+ conversationTitle = title ;
556+ } else if ("l" .equals (bundleEjson .type )) {
557+ // For omnichannel, use sender name if available, otherwise fall back to title
558+ conversationTitle = (bundleEjson .sender != null && bundleEjson .sender .name != null && !bundleEjson .sender .name .isEmpty ())
559+ ? bundleEjson .sender .name : title ;
560+ }
561+ }
562+ messageStyle .setConversationTitle (conversationTitle );
563563
564564 if (bundles != null ) {
565565 for (Bundle data : bundles ) {
0 commit comments