-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 푸시 알림 기능 #93
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
base: main
Are you sure you want to change the base?
feat: 푸시 알림 기능 #93
Changes from all 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 |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| /build | ||
| .idea/ | ||
| /release | ||
| /release | ||
| /google-services.json |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ plugins { | |
| alias(libs.plugins.kotlin.compose) | ||
| alias(libs.plugins.devtools.ksp) | ||
| alias(libs.plugins.hilt.android) | ||
| alias(libs.plugins.google.services) | ||
| } | ||
|
|
||
| val properties = Properties() | ||
|
|
@@ -35,7 +36,7 @@ android { | |
|
|
||
| buildTypes { | ||
| debug { | ||
| applicationIdSuffix = ".debug" | ||
| //applicationIdSuffix = ".debug" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| resValue("string", "app_name", "디버그 새길") | ||
| } | ||
| release { | ||
|
|
@@ -87,6 +88,10 @@ dependencies { | |
| // Timber for logging | ||
| implementation(libs.timber) | ||
|
|
||
| //FCM | ||
| implementation(platform(libs.firebase.bom)) | ||
| implementation(libs.firebase.messaging) | ||
|
|
||
| //모듈 의존 | ||
| implementation(project(":domain")) | ||
| implementation(project(":data")) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,28 @@ | ||
| package com.saegil.android | ||
|
|
||
| import android.app.Application | ||
| import com.google.firebase.Firebase | ||
| import com.google.firebase.messaging.messaging | ||
| import com.kakao.sdk.common.KakaoSdk | ||
| import dagger.hilt.android.HiltAndroidApp | ||
| import timber.log.Timber | ||
| import androidx.core.content.edit | ||
|
|
||
| @HiltAndroidApp | ||
| class App : Application() { | ||
| override fun onCreate() { | ||
| super.onCreate() | ||
| KakaoSdk.init(this, BuildConfig.NATIVE_APP_KEY) | ||
| Timber.plant(Timber.DebugTree()) | ||
| Firebase.messaging.token.addOnCompleteListener { task -> | ||
| if (task.isSuccessful) { | ||
| val deviceToken = task.result | ||
|
|
||
| getSharedPreferences("fcm", MODE_PRIVATE) | ||
| .edit { | ||
| putString("deviceToken", deviceToken) | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+17
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The task completion listener currently only handles the success case. If Firebase.messaging.token.addOnCompleteListener { task ->
if (task.isSuccessful) {
val deviceToken = task.result
getSharedPreferences("fcm", MODE_PRIVATE)
.edit {
putString("deviceToken", deviceToken)
}
} else {
Timber.w(task.exception, "Fetching FCM registration token failed")
}
} |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,54 @@ | ||||||||||
| package com.saegil.android | ||||||||||
|
|
||||||||||
| import android.app.NotificationChannel | ||||||||||
| import android.app.NotificationManager | ||||||||||
| import android.os.Build | ||||||||||
| import androidx.core.app.NotificationCompat | ||||||||||
| import com.google.firebase.messaging.FirebaseMessagingService | ||||||||||
| import com.google.firebase.messaging.RemoteMessage | ||||||||||
| import dagger.hilt.android.AndroidEntryPoint | ||||||||||
| import androidx.core.content.edit | ||||||||||
|
|
||||||||||
| @AndroidEntryPoint // Hilt 쓰는 경우 | ||||||||||
|
||||||||||
| @AndroidEntryPoint // Hilt 쓰는 경우 | |
| @AndroidEntryPoint // Used when Hilt is being used for dependency injection |
Copilot
AI
Jul 16, 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.
Default notification title is hardcoded in Korean. Consider using string resources for localization and consistency.
Copilot
AI
Jul 16, 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.
Default notification body is hardcoded in Korean. Consider using string resources for localization and consistency.
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.
The default values for the notification title and body are hardcoded. Use string resources (R.string...) for all user-facing text for maintainability and to support multiple languages in the future.
| val title = remoteMessage.notification?.title ?: "알림" | |
| val body = remoteMessage.notification?.body ?: "내용 없음" | |
| val title = remoteMessage.notification?.title ?: getString(R.string.notification_default_title) | |
| val body = remoteMessage.notification?.body ?: getString(R.string.notification_default_body) |
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.
Copilot
AI
Jul 16, 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.
Notification channel name is hardcoded in Korean. Consider using string resources for localization and consistency.
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.
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.
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.
Using System.currentTimeMillis().toInt() as a notification ID is not reliable because of potential collisions and integer overflow. Use a unique ID for each notification. If you don't need to update specific notifications, a random number is a simple and effective solution.
| notificationManager.notify(System.currentTimeMillis().toInt(), notification) | |
| notificationManager.notify(java.util.Random().nextInt(), notification) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,2 @@ | ||
| /build | ||
| /build | ||
| /google-services.json |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,4 +35,11 @@ class TokenDataSourceImpl @Inject constructor( | |
| TokenProto.getDefaultInstance() | ||
| } | ||
| } | ||
|
|
||
| override suspend fun getDeviceToken(): String { | ||
| val deviceToken = context | ||
| .getSharedPreferences("fcm", Context.MODE_PRIVATE) | ||
| .getString("deviceToken", null) | ||
|
Comment on lines
+40
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| return deviceToken ?: "" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,4 +36,6 @@ object HttpRoutes { | |
|
|
||
| const val NEWS_CATEGORIES = "$BASE_URL/api/v1/news/categories" | ||
|
|
||
| const val TEST = "$BASE_URL/api/v1/notifications/test" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,17 +1,27 @@ | ||||||||||||||
| package com.saegil.data.remote | ||||||||||||||
|
|
||||||||||||||
| import android.util.Log | ||||||||||||||
| import com.saegil.data.model.NewsItemDto | ||||||||||||||
| import com.saegil.data.remote.HttpRoutes.NEWS | ||||||||||||||
| import com.saegil.data.remote.HttpRoutes.TEST | ||||||||||||||
| import io.ktor.client.HttpClient | ||||||||||||||
| import io.ktor.client.call.body | ||||||||||||||
| import io.ktor.client.request.get | ||||||||||||||
| import io.ktor.client.request.parameter | ||||||||||||||
| import io.ktor.client.request.post | ||||||||||||||
| import javax.inject.Inject | ||||||||||||||
|
|
||||||||||||||
| class NewsServiceImpl @Inject constructor( | ||||||||||||||
| private val client: HttpClient | ||||||||||||||
| ): NewsService { | ||||||||||||||
|
|
||||||||||||||
| override suspend fun getNewsByTopics(): List<NewsItemDto> { | ||||||||||||||
| val testResponse = client.post(TEST) { | ||||||||||||||
| parameter("title", "테스트 알림") | ||||||||||||||
| parameter("body", "내용입니다.") | ||||||||||||||
| } | ||||||||||||||
| Log.d("경로",testResponse.toString()) | ||||||||||||||
|
||||||||||||||
| Log.d("경로",testResponse.body()) | ||||||||||||||
|
Comment on lines
+19
to
+24
|
||||||||||||||
| val testResponse = client.post(TEST) { | |
| parameter("title", "테스트 알림") | |
| parameter("body", "내용입니다.") | |
| } | |
| Log.d("경로",testResponse.toString()) | |
| Log.d("경로",testResponse.body()) |
Copilot
AI
Jul 16, 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.
Debug logging with Korean text should be removed from production code. Use proper logging framework like Timber if logging is needed.
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.
This block of code appears to be for testing the push notification functionality. This test code, including the API call and logging, should not be part of the production codebase. Remove it.
Use Timber for logging instead of android.util.Log, as Timber is already integrated into the project and provides more features like automatic tag generation and release build tree stripping.
return client.get(NEWS).body()
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.
Debug application ID suffix is commented out. This should be properly configured or removed if no longer needed, as it affects app installation and Firebase configuration.