package com.example.mycrib import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.os.Build import android.util.Log import androidx.core.app.NotificationCompat import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onNewToken(token: String) { super.onNewToken(token) Log.d(TAG, "New FCM token: $token") // Store token locally for registration with backend getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) .edit() .putString(KEY_FCM_TOKEN, token) .apply() // Send token to backend API if user is logged in // Note: In a real app, you might want to use WorkManager for reliable delivery CoroutineScope(Dispatchers.IO).launch { try { val authToken = com.mycrib.storage.TokenStorage.getToken() if (authToken != null) { val notificationApi = com.mycrib.shared.network.NotificationApi() val request = com.mycrib.shared.models.DeviceRegistrationRequest( registrationId = token, platform = "android" ) when (val result = notificationApi.registerDevice(authToken, request)) { is com.mycrib.shared.network.ApiResult.Success -> { Log.d(TAG, "Device registered successfully with new token") } is com.mycrib.shared.network.ApiResult.Error -> { Log.e(TAG, "Failed to register device with new token: ${result.message}") } is com.mycrib.shared.network.ApiResult.Loading, is com.mycrib.shared.network.ApiResult.Idle -> { // These states shouldn't occur for direct API calls } } } } catch (e: Exception) { Log.e(TAG, "Error registering device with new token", e) } } } override fun onMessageReceived(message: RemoteMessage) { super.onMessageReceived(message) Log.d(TAG, "Message received from: ${message.from}") // Check if message contains notification payload message.notification?.let { notification -> Log.d(TAG, "Notification: ${notification.title} - ${notification.body}") sendNotification( notification.title ?: "MyCrib", notification.body ?: "", message.data ) } // Check if message contains data payload if (message.data.isNotEmpty()) { Log.d(TAG, "Message data: ${message.data}") // If there's no notification payload, create one from data if (message.notification == null) { val title = message.data["title"] ?: "MyCrib" val body = message.data["body"] ?: "" sendNotification(title, body, message.data) } } } private fun sendNotification(title: String, body: String, data: Map) { val intent = Intent(this, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP // Add data to intent for handling when notification is clicked data.forEach { (key, value) -> putExtra(key, value) } } val pendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE } else { PendingIntent.FLAG_ONE_SHOT } val pendingIntent = PendingIntent.getActivity( this, 0, intent, pendingIntentFlags ) val channelId = getString(R.string.default_notification_channel_id) val notificationBuilder = NotificationCompat.Builder(this, channelId) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle(title) .setContentText(body) .setAutoCancel(true) .setContentIntent(pendingIntent) .setPriority(NotificationCompat.PRIORITY_HIGH) val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Create notification channel for Android O and above if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( channelId, "MyCrib Notifications", NotificationManager.IMPORTANCE_HIGH ).apply { description = "Notifications for tasks, residences, and warranties" } notificationManager.createNotificationChannel(channel) } notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()) } companion object { private const val TAG = "FCMService" private const val NOTIFICATION_ID = 0 private const val PREFS_NAME = "mycrib_prefs" private const val KEY_FCM_TOKEN = "fcm_token" fun getStoredToken(context: Context): String? { return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) .getString(KEY_FCM_TOKEN, null) } } }