-
Notifications
You must be signed in to change notification settings - Fork 0
feat - 푸시알림 등록 #54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat - 푸시알림 등록 #54
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -38,5 +38,6 @@ data class SignUpDTO( | |||||
|
|
||||||
| data class SignInDTO( | ||||||
| val email: String, | ||||||
| val password: String | ||||||
| val password: String, | ||||||
| val fcmToken: String, | ||||||
|
||||||
| val fcmToken: String, | |
| val fcmToken: String? = null, |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,8 +42,8 @@ class AuthRepository( | |
| return response.isSuccessful | ||
| } | ||
|
|
||
| suspend fun signIn(email:String,password:String) : SignInResponse?{ | ||
| val response = api.signIn(SignInDTO(email,password)).awaitResponse() | ||
| suspend fun signIn(email: String, password: String, fcmToken: String) : SignInResponse?{ | ||
|
||
| val response = api.signIn(SignInDTO(email, password, fcmToken)).awaitResponse() | ||
| Log.d("AuthRepository", "signIn success=${response.isSuccessful} code=${response.code()}") | ||
|
|
||
| return if (response.isSuccessful) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,65 @@ | ||||||||||||||
| package com.apptive.japkor.push | ||||||||||||||
|
|
||||||||||||||
| import android.app.PendingIntent | ||||||||||||||
| import android.content.Intent | ||||||||||||||
| import android.util.Log | ||||||||||||||
| import androidx.core.app.NotificationCompat | ||||||||||||||
| import androidx.core.app.NotificationManagerCompat | ||||||||||||||
| import com.apptive.japkor.MainActivity | ||||||||||||||
| import com.apptive.japkor.R | ||||||||||||||
| import com.apptive.japkor.data.local.DataStoreManager | ||||||||||||||
| 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 JapKorFirebaseMessagingService : FirebaseMessagingService() { | ||||||||||||||
|
|
||||||||||||||
| override fun onNewToken(token: String) { | ||||||||||||||
| Log.d(TAG, "New FCM token: $token") | ||||||||||||||
| CoroutineScope(Dispatchers.IO).launch { | ||||||||||||||
| DataStoreManager(applicationContext).saveFcmToken(token) | ||||||||||||||
|
||||||||||||||
| DataStoreManager(applicationContext).saveFcmToken(token) | |
| try { | |
| DataStoreManager(applicationContext).saveFcmToken(token) | |
| } catch (e: Exception) { | |
| Log.e(TAG, "Failed to save FCM token", e) | |
| } |
Copilot
AI
Dec 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing runtime permission check before displaying notifications. On Android 13 (API 33) and above, the POST_NOTIFICATIONS permission can be denied by users even after being requested. You should check if the permission is granted before calling notify(), otherwise a SecurityException could be thrown when the permission is denied.
Copilot
AI
Dec 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential notification ID collision and memory issue. Using System.currentTimeMillis().toInt() as the notification ID can lead to problems: (1) notifications arriving within the same millisecond will have the same ID and overwrite each other, (2) the cast to Int can cause collisions due to truncation. Consider using a more reliable approach such as a sequential counter or hash of the message ID.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,24 @@ | ||||||||||||||||||||||||||||||||||||||
| package com.apptive.japkor.push | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| import android.app.NotificationChannel | ||||||||||||||||||||||||||||||||||||||
| import android.app.NotificationManager | ||||||||||||||||||||||||||||||||||||||
| import android.content.Context | ||||||||||||||||||||||||||||||||||||||
| import android.os.Build | ||||||||||||||||||||||||||||||||||||||
| import com.apptive.japkor.R | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
| /** | |
| * Ensures that the app's default notification channel exists. | |
| * | |
| * This function should be called before posting notifications that rely on the | |
| * default FCM notification channel (for example during app startup or when | |
| * initializing push notification handling). | |
| * | |
| * On Android versions prior to O (API 26), this method is a no-op because | |
| * notification channels are not supported. On Android O and above, it will | |
| * create the default channel if it does not already exist. | |
| * | |
| * The operation is idempotent and safe to call multiple times: if the channel | |
| * already exists, the existing channel is left unchanged. | |
| * | |
| * @param context a [Context] used to access resources and the [NotificationManager]. | |
| */ |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -42,7 +42,8 @@ class LoginScreenViewModel( | |||||||||||||||
| Log.d("LoginVM", "signIn 호출: email=$email, password=$password") | ||||||||||||||||
|
|
||||||||||||||||
| try { | ||||||||||||||||
| val result = repository.signIn(email, password) | ||||||||||||||||
| val fcmToken = dataStore.getFcmToken().first() | ||||||||||||||||
|
||||||||||||||||
| val fcmToken = dataStore.getFcmToken().first() | |
| val storedFcmToken = dataStore.getFcmToken().first() | |
| val fcmToken = storedFcmToken.takeIf { it.isNotBlank() } | |
| if (fcmToken == null) { | |
| Log.w("LoginVM", "FCM token is not yet available; proceeding with sign-in without it.") | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,7 @@ | ||
| <resources> | ||
| <string name="app_name">en</string> | ||
| <string name="default_web_client_id">504737014677-p232m5s4jtl1t1v48ff6ir7gl3pcjokp.apps.googleusercontent.com</string> | ||
| </resources> | ||
| <string name="fcm_default_channel_id">fcm_default_channel</string> | ||
| <string name="fcm_default_channel_name">Default notifications</string> | ||
| <string name="fcm_default_channel_description">General notifications</string> | ||
| </resources> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Race condition between FCM token fetch and login. The FCM token is fetched asynchronously in MainActivity.onCreate() (lines 54-66) and saved via lifecycleScope.launch. However, if a user navigates to the login screen and attempts to sign in before this async operation completes, the fcmToken retrieved at line 45 could be empty. This creates a timing-dependent bug where login behavior differs based on how quickly the user navigates.