diff --git a/composeApp/src/androidMain/kotlin/com/tt/honeyDue/notifications/FcmService.kt b/composeApp/src/androidMain/kotlin/com/tt/honeyDue/notifications/FcmService.kt index a36f112..527b00b 100644 --- a/composeApp/src/androidMain/kotlin/com/tt/honeyDue/notifications/FcmService.kt +++ b/composeApp/src/androidMain/kotlin/com/tt/honeyDue/notifications/FcmService.kt @@ -1,6 +1,7 @@ package com.tt.honeyDue.notifications import android.app.PendingIntent +import android.content.Context import android.content.Intent import android.net.Uri import android.os.Build @@ -11,6 +12,13 @@ import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage import com.tt.honeyDue.MainActivity import com.tt.honeyDue.R +import com.tt.honeyDue.models.DeviceRegistrationRequest +import com.tt.honeyDue.network.ApiResult +import com.tt.honeyDue.network.NotificationApi +import com.tt.honeyDue.storage.TokenStorage +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch /** * New-generation FirebaseMessagingService built for iOS parity. Routes each @@ -19,21 +27,52 @@ import com.tt.honeyDue.R * messageId as the notification id so duplicate redeliveries replace (not * stack). * - * This lives alongside the legacy `MyFirebaseMessagingService`. The manifest - * currently wires FCM to this service; the legacy class is retained until - * its call sites (widget + notification-action flows) are migrated. + * This is the sole MESSAGING_EVENT handler after the deferred-cleanup pass: + * the manifest no longer wires the legacy `MyFirebaseMessagingService`, and + * [onNewToken] now carries the token-registration path that used to live + * there (auth-token guard + device-id + platform="android"). */ class FcmService : FirebaseMessagingService() { override fun onNewToken(token: String) { super.onNewToken(token) Log.d(TAG, "FCM token refreshed (len=${token.length})") - // TODO: wire token registration to APILayer.registerDevice. - // Registration is already performed by MyFirebaseMessagingService.onNewToken - // at composeApp/src/androidMain/kotlin/com/tt/honeyDue/MyFirebaseMessagingService.kt:20-65 - // using NotificationApi().registerDevice(...). When that legacy service is - // removed, port the same call path here (auth-token guard + device-id + - // platform="android"). P4 Stream N scope is receive-side only. + + // Store token locally so the rest of the app can find it on demand. + getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) + .edit() + .putString(KEY_FCM_TOKEN, token) + .apply() + + // Register with backend only if the user is logged in. Log-only on + // failure — FCM will re-invoke onNewToken on next rotation. + CoroutineScope(Dispatchers.IO).launch { + try { + val authToken = TokenStorage.getToken() ?: return@launch + val deviceId = android.provider.Settings.Secure.getString( + applicationContext.contentResolver, + android.provider.Settings.Secure.ANDROID_ID + ) + val request = DeviceRegistrationRequest( + deviceId = deviceId, + registrationId = token, + platform = "android", + name = android.os.Build.MODEL + ) + when (val result = NotificationApi().registerDevice(authToken, request)) { + is ApiResult.Success -> + Log.d(TAG, "Device registered successfully with new token") + is ApiResult.Error -> + Log.e(TAG, "Failed to register device with new token: ${result.message}") + is ApiResult.Loading, + is 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) { @@ -205,5 +244,13 @@ class FcmService : FirebaseMessagingService() { const val EXTRA_TASK_ID = "fcm_task_id" const val EXTRA_RESIDENCE_ID = "fcm_residence_id" const val EXTRA_TYPE = "fcm_type" + + private const val PREFS_NAME = "honeydue_prefs" + private const val KEY_FCM_TOKEN = "fcm_token" + + /** Compatibility helper — mirrors the old MyFirebaseMessagingService API. */ + fun getStoredToken(context: Context): String? = + context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) + .getString(KEY_FCM_TOKEN, null) } }