@@ -16,13 +16,53 @@ import com.google.firebase.messaging.RemoteMessage
1616
1717class MyFirebaseMessagingService : FirebaseMessagingService () {
1818
19+ companion object {
20+ private const val TAG = " FCMService"
21+ private const val CHANNEL_ID = " app_update_channel"
22+ private const val NOTIFICATION_ID = 1001
23+ }
24+
1925 override fun onMessageReceived (remoteMessage : RemoteMessage ) {
2026 super .onMessageReceived(remoteMessage)
2127
22- // Check if message contains data payload for update notification
23- remoteMessage.data.isNotEmpty().let {
24- if (remoteMessage.data[" type" ] == " app_update" ) {
25- handleUpdateNotification(remoteMessage.data)
28+ Log .d(TAG , " Message received from: ${remoteMessage.from} " )
29+
30+ // Handle both notification and data payloads
31+ // This ensures the notification works in all app states
32+
33+ // Check if message contains a notification payload (background/terminated state)
34+ remoteMessage.notification?.let {
35+ Log .d(TAG , " Notification Title: ${it.title} " )
36+ Log .d(TAG , " Notification Body: ${it.body} " )
37+
38+ // When app is in background/terminated, system handles notification
39+ // but we can still process data payload
40+ if (remoteMessage.data.isNotEmpty()) {
41+ handleDataPayload(remoteMessage.data)
42+ }
43+ }
44+
45+ // Check if message contains a data payload (foreground state)
46+ if (remoteMessage.data.isNotEmpty()) {
47+ Log .d(TAG , " Data Payload: ${remoteMessage.data} " )
48+
49+ // When app is in foreground, we must manually show notification
50+ if (isAppInForeground()) {
51+ handleDataPayload(remoteMessage.data)
52+ }
53+ }
54+ }
55+
56+ private fun handleDataPayload (data : Map <String , String >) {
57+ val type = data[" type" ]
58+
59+ when (type) {
60+ " app_update" -> handleUpdateNotification(data)
61+ " general" -> handleGeneralNotification(data)
62+ else -> {
63+ // Handle unknown notification types
64+ Log .w(TAG , " Unknown notification type: $type " )
65+ handleGeneralNotification(data)
2666 }
2767 }
2868 }
@@ -31,46 +71,57 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
3171 val title = data[" title" ] ? : " Update Available"
3272 val message = data[" message" ] ? : " A new version of the app is available"
3373 val version = data[" version" ] ? : " "
74+ val updateUrl = data[" update_url" ] // Optional custom URL
75+
76+ showUpdateNotification(title, message, version, updateUrl)
77+ }
78+
79+ private fun handleGeneralNotification (data : Map <String , String >) {
80+ val title = data[" title" ] ? : " Notification"
81+ val message = data[" message" ] ? : " "
3482
35- showUpdateNotification (title, message, version )
83+ showGeneralNotification (title, message)
3684 }
3785
38- private fun showUpdateNotification (title : String , message : String , version : String ) {
86+ private fun showUpdateNotification (
87+ title : String ,
88+ message : String ,
89+ version : String ,
90+ customUrl : String? = null
91+ ) {
3992 val notificationManager = getSystemService(Context .NOTIFICATION_SERVICE ) as NotificationManager
40- val channelId = " app_update_channel"
4193
42- // Create notification channel (for Android O and above)
43- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
44- val channel = NotificationChannel (
45- channelId,
46- " App Updates" ,
47- NotificationManager .IMPORTANCE_HIGH
48- ).apply {
49- description = " Notifications for app updates"
50- enableLights(true )
51- lightColor = Color .BLUE
52- enableVibration(true )
53- }
54- notificationManager.createNotificationChannel(channel)
94+ createNotificationChannel(notificationManager)
95+
96+ // Create intent to open Play Store or custom URL
97+ val intent = if (customUrl != null ) {
98+ createCustomUrlIntent(customUrl)
99+ } else {
100+ createPlayStoreIntent()
55101 }
56102
57- // Create intent to open Play Store
58- val playStoreIntent = createPlayStoreIntent()
59103 val pendingIntent = PendingIntent .getActivity(
60104 this ,
61105 0 ,
62- playStoreIntent ,
106+ intent ,
63107 PendingIntent .FLAG_UPDATE_CURRENT or PendingIntent .FLAG_IMMUTABLE
64108 )
65109
66- // Build notification
67- val notification = NotificationCompat .Builder (this , channelId)
68- .setSmallIcon(R .drawable.ic_update) // Add your update icon
110+ // Build notification with full details
111+ val fullMessage = if (version.isNotEmpty()) {
112+ " $message \n Version: $version "
113+ } else {
114+ message
115+ }
116+
117+ val notification = NotificationCompat .Builder (this , CHANNEL_ID )
118+ .setSmallIcon(R .drawable.ic_update)
69119 .setContentTitle(title)
70120 .setContentText(message)
71- .setStyle(NotificationCompat .BigTextStyle ().bigText(message ))
121+ .setStyle(NotificationCompat .BigTextStyle ().bigText(fullMessage ))
72122 .setAutoCancel(true )
73123 .setPriority(NotificationCompat .PRIORITY_HIGH )
124+ .setDefaults(NotificationCompat .DEFAULT_ALL )
74125 .setContentIntent(pendingIntent)
75126 .addAction(
76127 R .drawable.ic_download,
@@ -79,30 +130,118 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
79130 )
80131 .build()
81132
82- notificationManager.notify(1001 , notification)
133+ notificationManager.notify(NOTIFICATION_ID , notification)
134+ }
135+
136+ private fun showGeneralNotification (title : String , message : String ) {
137+ val notificationManager = getSystemService(Context .NOTIFICATION_SERVICE ) as NotificationManager
138+
139+ createNotificationChannel(notificationManager)
140+
141+ // Create intent to open the app
142+ val intent = packageManager.getLaunchIntentForPackage(packageName)?.apply {
143+ flags = Intent .FLAG_ACTIVITY_NEW_TASK or Intent .FLAG_ACTIVITY_CLEAR_TOP
144+ }
145+
146+ val pendingIntent = PendingIntent .getActivity(
147+ this ,
148+ 0 ,
149+ intent,
150+ PendingIntent .FLAG_UPDATE_CURRENT or PendingIntent .FLAG_IMMUTABLE
151+ )
152+
153+ val notification = NotificationCompat .Builder (this , CHANNEL_ID )
154+ .setSmallIcon(R .drawable.ic_notification)
155+ .setContentTitle(title)
156+ .setContentText(message)
157+ .setStyle(NotificationCompat .BigTextStyle ().bigText(message))
158+ .setAutoCancel(true )
159+ .setPriority(NotificationCompat .PRIORITY_HIGH )
160+ .setDefaults(NotificationCompat .DEFAULT_ALL )
161+ .setContentIntent(pendingIntent)
162+ .build()
163+
164+ notificationManager.notify(NOTIFICATION_ID + 1 , notification)
165+ }
166+
167+ private fun createNotificationChannel (notificationManager : NotificationManager ) {
168+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
169+ val channel = NotificationChannel (
170+ CHANNEL_ID ,
171+ " App Updates & Notifications" ,
172+ NotificationManager .IMPORTANCE_HIGH
173+ ).apply {
174+ description = " Notifications for app updates and important messages"
175+ enableLights(true )
176+ lightColor = Color .BLUE
177+ enableVibration(true )
178+ setShowBadge(true )
179+ }
180+ notificationManager.createNotificationChannel(channel)
181+ }
83182 }
84183
85184 private fun createPlayStoreIntent (): Intent {
86185 val packageName = packageName
87186
88- // Try to open Play Store app first
89187 return try {
90- Intent (Intent .ACTION_VIEW , Uri .parse(" market://details?id=$packageName " ))
188+ Intent (Intent .ACTION_VIEW , Uri .parse(" market://details?id=$packageName " )).apply {
189+ addFlags(Intent .FLAG_ACTIVITY_NEW_TASK )
190+ }
91191 } catch (e: ActivityNotFoundException ) {
92- // Fallback to Play Store website
93- Intent (Intent .ACTION_VIEW , Uri .parse(" https://play.google.com/store/apps/details?id=$packageName " ))
192+ Intent (Intent .ACTION_VIEW , Uri .parse(" https://play.google.com/store/apps/details?id=$packageName " )).apply {
193+ addFlags(Intent .FLAG_ACTIVITY_NEW_TASK )
194+ }
195+ }
196+ }
197+
198+ private fun createCustomUrlIntent (url : String ): Intent {
199+ return Intent (Intent .ACTION_VIEW , Uri .parse(url)).apply {
200+ addFlags(Intent .FLAG_ACTIVITY_NEW_TASK )
201+ }
202+ }
203+
204+ private fun isAppInForeground (): Boolean {
205+ val activityManager = getSystemService(Context .ACTIVITY_SERVICE ) as android.app.ActivityManager
206+ val appProcesses = activityManager.runningAppProcesses ? : return false
207+
208+ return appProcesses.any {
209+ it.importance == android.app.ActivityManager .RunningAppProcessInfo .IMPORTANCE_FOREGROUND
210+ && it.processName == packageName
94211 }
95212 }
96213
97214 override fun onNewToken (token : String ) {
98215 super .onNewToken(token)
216+ Log .d(TAG , " New FCM token: $token " )
217+
99218 // Send token to your server
100219 sendTokenToServer(token)
220+
221+ // Store token locally
222+ saveTokenLocally(token)
101223 }
102224
103225 private fun sendTokenToServer (token : String ) {
104- // Implement your server communication here
105- // This is where you'd send the FCM token to your backend
106- Log .d(" FCM" , " New token: $token " )
226+ // TODO: Implement your server communication here
227+ // Example: Use Retrofit or OkHttp to send token to your backend
228+ Log .d(TAG , " Sending token to server: $token " )
229+
230+ // Example implementation:
231+ // RetrofitClient.instance.updateFcmToken(token)
232+ // .enqueue(object : Callback<Response> {
233+ // override fun onResponse(call: Call<Response>, response: Response<Response>) {
234+ // Log.d(TAG, "Token sent successfully")
235+ // }
236+ // override fun onFailure(call: Call<Response>, t: Throwable) {
237+ // Log.e(TAG, "Failed to send token", t)
238+ // }
239+ // })
240+ }
241+
242+ private fun saveTokenLocally (token : String ) {
243+ val sharedPreferences = getSharedPreferences(" FCM_PREFS" , Context .MODE_PRIVATE )
244+ sharedPreferences.edit().putString(" FCM_TOKEN" , token).apply ()
245+ Log .d(TAG , " Token saved locally" )
107246 }
108247}
0 commit comments