4040import chat .rocket .reactnative .R ;
4141
4242public class CustomPushNotification extends PushNotification {
43+ private static final String TAG = "RocketChat.CustomPush" ;
44+ private static final boolean ENABLE_VERBOSE_LOGS = BuildConfig .DEBUG ;
45+
4346 public static ReactApplicationContext reactApplicationContext ;
4447 final NotificationManager notificationManager ;
4548
@@ -65,29 +68,111 @@ public static void clearMessages(int notId) {
6568
6669 @ Override
6770 public void onReceived () throws InvalidNotificationException {
71+ // Check if React is ready - needed for MMKV access (avatars, encryption, message-id-only)
72+ if (!mAppLifecycleFacade .isReactInitialized ()) {
73+ android .util .Log .w (TAG , "React not initialized yet, waiting before processing notification..." );
74+
75+ // Wait for React to initialize with timeout
76+ new Thread (() -> {
77+ int attempts = 0 ;
78+ int maxAttempts = 50 ; // 5 seconds total (50 * 100ms)
79+
80+ while (!mAppLifecycleFacade .isReactInitialized () && attempts < maxAttempts ) {
81+ try {
82+ Thread .sleep (100 ); // Wait 100ms
83+ attempts ++;
84+
85+ if (attempts % 10 == 0 && ENABLE_VERBOSE_LOGS ) {
86+ android .util .Log .d (TAG , "Still waiting for React initialization... (" + (attempts * 100 ) + "ms elapsed)" );
87+ }
88+ } catch (InterruptedException e ) {
89+ android .util .Log .e (TAG , "Wait interrupted" , e );
90+ Thread .currentThread ().interrupt ();
91+ return ;
92+ }
93+ }
94+
95+ if (mAppLifecycleFacade .isReactInitialized ()) {
96+ android .util .Log .i (TAG , "React initialized after " + (attempts * 100 ) + "ms, proceeding with notification" );
97+ handleNotification ();
98+ } else {
99+ android .util .Log .e (TAG , "Timeout waiting for React initialization after " + (maxAttempts * 100 ) + "ms, processing without MMKV" );
100+ handleNotification ();
101+ }
102+ }).start ();
103+
104+ return ; // Exit early, notification will be processed in the thread
105+ }
106+
107+ if (ENABLE_VERBOSE_LOGS ) {
108+ android .util .Log .d (TAG , "React already initialized, proceeding with notification" );
109+ }
110+ handleNotification ();
111+ }
112+
113+ private void handleNotification () {
68114 Bundle received = mNotificationProps .asBundle ();
69115 Ejson receivedEjson = safeFromJson (received .getString ("ejson" , "{}" ), Ejson .class );
70116
71117 if (receivedEjson != null && receivedEjson .notificationType != null && receivedEjson .notificationType .equals ("message-id-only" )) {
72- android .util .Log .d ("RocketChat.CustomPush" , "Detected message-id-only notification, will fetch full content from server" );
73- notificationLoad (receivedEjson , new Callback () {
74- @ Override
75- public void call (@ Nullable Bundle bundle ) {
76- if (bundle != null ) {
77- android .util .Log .d ("RocketChat.CustomPush" , "Successfully loaded notification content from server, updating notification props" );
78- mNotificationProps = createProps (bundle );
79- } else {
80- android .util .Log .w ("RocketChat.CustomPush" , "Failed to load notification content from server, will display placeholder notification" );
81- }
82- }
83- });
118+ android .util .Log .d (TAG , "Detected message-id-only notification, will fetch full content from server" );
119+ loadNotificationAndProcess (receivedEjson );
120+ return ; // Exit early, notification will be processed in callback
84121 }
85122
123+ // For non-message-id-only notifications, process immediately
124+ processNotification ();
125+ }
126+
127+ private void loadNotificationAndProcess (Ejson ejson ) {
128+ notificationLoad (ejson , new Callback () {
129+ @ Override
130+ public void call (@ Nullable Bundle bundle ) {
131+ if (bundle != null ) {
132+ android .util .Log .d (TAG , "Successfully loaded notification content from server, updating notification props" );
133+
134+ if (ENABLE_VERBOSE_LOGS ) {
135+ // BEFORE createProps
136+ android .util .Log .d (TAG , "[BEFORE createProps] bundle.notificationLoaded=" + bundle .getBoolean ("notificationLoaded" , false ));
137+ android .util .Log .d (TAG , "[BEFORE createProps] bundle.title=" + (bundle .getString ("title" ) != null ? "[present]" : "[null]" ));
138+ android .util .Log .d (TAG , "[BEFORE createProps] bundle.message length=" + (bundle .getString ("message" ) != null ? bundle .getString ("message" ).length () : 0 ));
139+ android .util .Log .d (TAG , "[BEFORE createProps] bundle has ejson=" + (bundle .getString ("ejson" ) != null ));
140+ }
141+
142+ mNotificationProps = createProps (bundle );
143+
144+ if (ENABLE_VERBOSE_LOGS ) {
145+ // AFTER createProps - verify it worked
146+ Bundle verifyBundle = mNotificationProps .asBundle ();
147+ android .util .Log .d (TAG , "[AFTER createProps] mNotificationProps.notificationLoaded=" + verifyBundle .getBoolean ("notificationLoaded" , false ));
148+ android .util .Log .d (TAG , "[AFTER createProps] mNotificationProps.title=" + (verifyBundle .getString ("title" ) != null ? "[present]" : "[null]" ));
149+ android .util .Log .d (TAG , "[AFTER createProps] mNotificationProps.message length=" + (verifyBundle .getString ("message" ) != null ? verifyBundle .getString ("message" ).length () : 0 ));
150+ android .util .Log .d (TAG , "[AFTER createProps] mNotificationProps has ejson=" + (verifyBundle .getString ("ejson" ) != null ));
151+ }
152+ } else {
153+ android .util .Log .w (TAG , "Failed to load notification content from server, will display placeholder notification" );
154+ }
155+
156+ processNotification ();
157+ }
158+ });
159+ }
160+
161+ private void processNotification () {
86162 // We should re-read these values since that can be changed by notificationLoad
87163 Bundle bundle = mNotificationProps .asBundle ();
88164 Ejson loadedEjson = safeFromJson (bundle .getString ("ejson" , "{}" ), Ejson .class );
89165 String notId = bundle .getString ("notId" , "1" );
90166
167+ if (ENABLE_VERBOSE_LOGS ) {
168+ android .util .Log .d (TAG , "[onReceived processing] notId=" + notId );
169+ android .util .Log .d (TAG , "[onReceived processing] bundle.notificationLoaded=" + bundle .getBoolean ("notificationLoaded" , false ));
170+ android .util .Log .d (TAG , "[onReceived processing] bundle.title=" + (bundle .getString ("title" ) != null ? "[present]" : "[null]" ));
171+ android .util .Log .d (TAG , "[onReceived processing] bundle.message length=" + (bundle .getString ("message" ) != null ? bundle .getString ("message" ).length () : 0 ));
172+ android .util .Log .d (TAG , "[onReceived processing] loadedEjson.notificationType=" + (loadedEjson != null ? loadedEjson .notificationType : "null" ));
173+ android .util .Log .d (TAG , "[onReceived processing] loadedEjson.sender=" + (loadedEjson != null && loadedEjson .sender != null ? loadedEjson .sender .username : "null" ));
174+ }
175+
91176 if (notificationMessages .get (notId ) == null ) {
92177 notificationMessages .put (notId , new ArrayList <Bundle >());
93178 }
@@ -107,12 +192,23 @@ public void call(@Nullable Bundle bundle) {
107192 bundle .putLong ("time" , new Date ().getTime ());
108193 bundle .putString ("username" , hasSender ? loadedEjson .sender .username : title );
109194 bundle .putString ("senderId" , hasSender ? loadedEjson .sender ._id : "1" );
110- bundle .putString ("avatarUri" , loadedEjson != null ? loadedEjson .getAvatarUri () : null );
195+
196+ String avatarUri = loadedEjson != null ? loadedEjson .getAvatarUri () : null ;
197+ if (ENABLE_VERBOSE_LOGS ) {
198+ android .util .Log .d (TAG , "[processNotification] avatarUri=" + (avatarUri != null ? "[present]" : "[null]" ));
199+ }
200+ bundle .putString ("avatarUri" , avatarUri );
111201
112202 if (loadedEjson != null && loadedEjson .notificationType instanceof String && loadedEjson .notificationType .equals ("videoconf" )) {
113203 notifyReceivedToJS ();
114204 } else {
205+ if (ENABLE_VERBOSE_LOGS ) {
206+ android .util .Log .d (TAG , "[Before add to notificationMessages] notId=" + notId + ", bundle.message length=" + (bundle .getString ("message" ) != null ? bundle .getString ("message" ).length () : 0 ) + ", bundle.notificationLoaded=" + bundle .getBoolean ("notificationLoaded" , false ));
207+ }
115208 notificationMessages .get (notId ).add (bundle );
209+ if (ENABLE_VERBOSE_LOGS ) {
210+ android .util .Log .d (TAG , "[After add] notificationMessages[" + notId + "].size=" + notificationMessages .get (notId ).size ());
211+ }
116212 postNotification (Integer .parseInt (notId ));
117213 notifyReceivedToJS ();
118214 }
@@ -137,6 +233,16 @@ protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
137233 Boolean notificationLoaded = bundle .getBoolean ("notificationLoaded" , false );
138234 Ejson ejson = safeFromJson (bundle .getString ("ejson" , "{}" ), Ejson .class );
139235
236+ if (ENABLE_VERBOSE_LOGS ) {
237+ android .util .Log .d (TAG , "[getNotificationBuilder] notId=" + notId );
238+ android .util .Log .d (TAG , "[getNotificationBuilder] notificationLoaded=" + notificationLoaded );
239+ android .util .Log .d (TAG , "[getNotificationBuilder] title=" + (title != null ? "[present]" : "[null]" ));
240+ android .util .Log .d (TAG , "[getNotificationBuilder] message length=" + (message != null ? message .length () : 0 ));
241+ android .util .Log .d (TAG , "[getNotificationBuilder] ejson=" + (ejson != null ? "present" : "null" ));
242+ android .util .Log .d (TAG , "[getNotificationBuilder] ejson.notificationType=" + (ejson != null ? ejson .notificationType : "null" ));
243+ android .util .Log .d (TAG , "[getNotificationBuilder] ejson.sender=" + (ejson != null && ejson .sender != null ? ejson .sender .username : "null" ));
244+ }
245+
140246 notification
141247 .setContentTitle (title )
142248 .setContentText (message )
@@ -153,12 +259,13 @@ protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
153259
154260 // if notificationType is null (RC < 3.5) or notificationType is different of message-id-only or notification was loaded successfully
155261 if (ejson == null || ejson .notificationType == null || !ejson .notificationType .equals ("message-id-only" ) || notificationLoaded ) {
262+ android .util .Log .i (TAG , "[getNotificationBuilder] ✅ Rendering FULL notification style (ejson=" + (ejson != null ) + ", notificationType=" + (ejson != null ? ejson .notificationType : "null" ) + ", notificationLoaded=" + notificationLoaded + ")" );
156263 notificationStyle (notification , notificationId , bundle );
157264 notificationReply (notification , notificationId , bundle );
158265
159266 // message couldn't be loaded from server (Fallback notification)
160267 } else {
161- android .util .Log .w ("RocketChat.CustomPush" , "Displaying fallback notification for message-id-only (content failed to load from server )" );
268+ android .util .Log .w (TAG , "[getNotificationBuilder] ⚠️ Rendering FALLBACK notification (ejson=" + ( ejson != null ) + ", notificationType=" + ( ejson != null ? ejson . notificationType : "null" ) + ", notificationLoaded=" + notificationLoaded + " )" );
162269 Gson gson = new Gson ();
163270 // iterate over the current notification ids to dismiss fallback notifications from same server
164271 for (Map .Entry <String , List <Bundle >> bundleList : notificationMessages .entrySet ()) {
@@ -173,7 +280,9 @@ protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
173280 String id = not .getString ("notId" );
174281 // cancel this notification
175282 notificationManager .cancel (Integer .parseInt (id ));
176- android .util .Log .d ("RocketChat.CustomPush" , "Cancelled previous fallback notification from same server" );
283+ if (ENABLE_VERBOSE_LOGS ) {
284+ android .util .Log .d (TAG , "Cancelled previous fallback notification from same server" );
285+ }
177286 }
178287 }
179288 }
@@ -190,14 +299,42 @@ private void notifyReceivedToJS() {
190299 }
191300
192301 private Bitmap getAvatar (String uri ) {
302+ if (uri == null || uri .isEmpty ()) {
303+ if (ENABLE_VERBOSE_LOGS ) {
304+ android .util .Log .w (TAG , "getAvatar called with null/empty URI" );
305+ }
306+ return largeIcon ();
307+ }
308+
309+ // Sanitize URL for logging (remove query params with tokens)
310+ String sanitizedUri = uri ;
311+ int queryStart = uri .indexOf ("?" );
312+ if (queryStart != -1 ) {
313+ sanitizedUri = uri .substring (0 , queryStart ) + "?[auth_params]" ;
314+ }
315+
316+ if (ENABLE_VERBOSE_LOGS ) {
317+ android .util .Log .d (TAG , "Fetching avatar from: " + sanitizedUri );
318+ }
319+
193320 try {
194- return Glide .with (mContext )
321+ Bitmap avatar = Glide .with (mContext )
195322 .asBitmap ()
196323 .apply (RequestOptions .bitmapTransform (new RoundedCorners (10 )))
197324 .load (uri )
198325 .submit (100 , 100 )
199326 .get ();
327+
328+ if (avatar != null ) {
329+ if (ENABLE_VERBOSE_LOGS ) {
330+ android .util .Log .d (TAG , "Successfully loaded avatar" );
331+ }
332+ } else {
333+ android .util .Log .w (TAG , "Avatar loaded but is null" );
334+ }
335+ return avatar != null ? avatar : largeIcon ();
200336 } catch (final ExecutionException | InterruptedException e ) {
337+ android .util .Log .e (TAG , "Failed to fetch avatar: " + e .getMessage (), e );
201338 return largeIcon ();
202339 }
203340 }
@@ -267,6 +404,15 @@ private void notificationColor(Notification.Builder notification) {
267404 private void notificationStyle (Notification .Builder notification , int notId , Bundle bundle ) {
268405 List <Bundle > bundles = notificationMessages .get (Integer .toString (notId ));
269406
407+ if (ENABLE_VERBOSE_LOGS ) {
408+ android .util .Log .d (TAG , "[notificationStyle] notId=" + notId + ", bundles=" + (bundles != null ? bundles .size () : "null" ));
409+ if (bundles != null && bundles .size () > 0 ) {
410+ Bundle firstBundle = bundles .get (0 );
411+ android .util .Log .d (TAG , "[notificationStyle] first bundle.message length=" + (firstBundle .getString ("message" ) != null ? firstBundle .getString ("message" ).length () : 0 ));
412+ android .util .Log .d (TAG , "[notificationStyle] first bundle.notificationLoaded=" + firstBundle .getBoolean ("notificationLoaded" , false ));
413+ }
414+ }
415+
270416 if (Build .VERSION .SDK_INT < Build .VERSION_CODES .N ) {
271417 Notification .InboxStyle messageStyle = new Notification .InboxStyle ();
272418 if (bundles != null ) {
0 commit comments