1+ package com.texthip.thip.data.manager
2+
3+ import android.content.Context
4+ import android.util.Log
5+ import androidx.datastore.core.DataStore
6+ import androidx.datastore.preferences.core.Preferences
7+ import androidx.datastore.preferences.core.edit
8+ import androidx.datastore.preferences.core.stringPreferencesKey
9+ import com.google.firebase.messaging.FirebaseMessaging
10+ import com.texthip.thip.data.repository.NotificationRepository
11+ import com.texthip.thip.utils.auth.getAppScopeDeviceId
12+ import com.texthip.thip.utils.permission.NotificationPermissionUtils
13+ import dagger.hilt.android.qualifiers.ApplicationContext
14+ import kotlinx.coroutines.flow.first
15+ import kotlinx.coroutines.flow.map
16+ import kotlinx.coroutines.suspendCancellableCoroutine
17+ import kotlin.coroutines.resume
18+ import kotlin.coroutines.resumeWithException
19+ import javax.inject.Inject
20+ import javax.inject.Singleton
21+
22+ @Singleton
23+ class FcmTokenManager @Inject constructor(
24+ private val dataStore : DataStore <Preferences >,
25+ private val notificationRepository : NotificationRepository ,
26+ @param:ApplicationContext private val context : Context
27+ ) {
28+
29+ companion object {
30+ private val FCM_TOKEN_KEY = stringPreferencesKey(" fcm_token" )
31+ }
32+
33+ suspend fun handleNewToken (newToken : String ) {
34+ val storedToken = getFcmTokenOnce()
35+
36+ if (storedToken != newToken) {
37+ Log .d(" FCM" , " Token updated" )
38+
39+ saveFcmToken(newToken)
40+ sendTokenToServer(newToken)
41+ }
42+ }
43+
44+ suspend fun sendCurrentTokenIfExists () {
45+ val storedFcmToken = getFcmTokenOnce()
46+
47+ if (storedFcmToken != null ) {
48+ sendTokenToServer(storedFcmToken)
49+ } else {
50+ // 저장된 토큰이 없으면 Firebase에서 직접 가져와서 저장하고 전송
51+ try {
52+ val token = fetchCurrentToken()
53+ saveFcmToken(token)
54+ sendTokenToServer(token)
55+ } catch (e: Exception ) {
56+ Log .e(" FCM" , " Failed to fetch and send current token" , e)
57+ }
58+ }
59+ }
60+
61+ private suspend fun fetchCurrentToken (): String = suspendCancellableCoroutine { continuation ->
62+ try {
63+ FirebaseMessaging .getInstance().token.addOnCompleteListener { task ->
64+ when {
65+ task.isSuccessful -> {
66+ val token = task.result
67+ if (token != null ) {
68+ continuation.resume(token)
69+ } else {
70+ continuation.resumeWithException(IllegalStateException (" FCM token is null" ))
71+ }
72+ }
73+ else -> {
74+ val exception = task.exception ? : Exception (" Unknown error fetching FCM token" )
75+ Log .w(" FCM" , " Failed to fetch token" , exception)
76+ continuation.resumeWithException(exception)
77+ }
78+ }
79+ }
80+ } catch (e: Exception ) {
81+ Log .e(" FCM" , " Error fetching FCM token" , e)
82+ continuation.resumeWithException(e)
83+ }
84+ }
85+
86+ // FCM 토큰 로컬 저장 관리
87+ private suspend fun saveFcmToken (token : String ) {
88+ dataStore.edit { prefs -> prefs[FCM_TOKEN_KEY ] = token }
89+ }
90+
91+ private suspend fun getFcmTokenOnce (): String? {
92+ return dataStore.data.map { prefs -> prefs[FCM_TOKEN_KEY ] }.first()
93+ }
94+
95+ private suspend fun sendTokenToServer (token : String ) {
96+ // 알림 권한이 없으면 토큰을 서버에 전송하지 않음
97+ if (! NotificationPermissionUtils .isNotificationPermissionGranted(context)) {
98+ Log .w(" FCM" , " Notification permission not granted, skipping token registration" )
99+ return
100+ }
101+
102+ val deviceId = context.getAppScopeDeviceId()
103+ notificationRepository.registerFcmToken(deviceId, token)
104+ .onSuccess {
105+ Log .d(" FCM" , " Token sent successfully" )
106+ }
107+ .onFailure { exception ->
108+ Log .e(" FCM" , " Failed to send token" , exception)
109+ }
110+ }
111+ }
0 commit comments