diff --git a/app/.gitignore b/app/.gitignore
index 89499d03..3ca7ccf5 100644
--- a/app/.gitignore
+++ b/app/.gitignore
@@ -1,3 +1,4 @@
/build
.idea/
-/release
\ No newline at end of file
+/release
+/google-services.json
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 85cfe743..8fe99eb8 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -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"
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"))
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8663486b..2935f286 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,6 +6,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/saegil/android/App.kt b/app/src/main/java/com/saegil/android/App.kt
index 7e5f43ba..9588e76d 100644
--- a/app/src/main/java/com/saegil/android/App.kt
+++ b/app/src/main/java/com/saegil/android/App.kt
@@ -1,9 +1,12 @@
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() {
@@ -11,5 +14,15 @@ class App : Application() {
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)
+ }
+ }
+ }
}
}
diff --git a/app/src/main/java/com/saegil/android/SaegilFirebaseMessagingService.kt b/app/src/main/java/com/saegil/android/SaegilFirebaseMessagingService.kt
new file mode 100644
index 00000000..30f771d5
--- /dev/null
+++ b/app/src/main/java/com/saegil/android/SaegilFirebaseMessagingService.kt
@@ -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 쓰는 경우
+class SaegilFirebaseMessagingService : FirebaseMessagingService() {
+
+ override fun onNewToken(token: String) {
+ super.onNewToken(token)
+
+ getSharedPreferences("fcm", MODE_PRIVATE)
+ .edit {
+ putString("deviceToken", token)
+ }
+ }
+
+ override fun onMessageReceived(remoteMessage: RemoteMessage) {
+ super.onMessageReceived(remoteMessage)
+
+ val title = remoteMessage.notification?.title ?: "알림"
+ val body = remoteMessage.notification?.body ?: "내용 없음"
+ showNotification(title, body)
+ }
+
+ private fun showNotification(title: String, message: String) {
+ val channelId = "default_channel"
+
+ val notificationManager =
+ getSystemService(NOTIFICATION_SERVICE) as NotificationManager
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val channel = NotificationChannel(
+ channelId,
+ "기본 알림 채널",
+ NotificationManager.IMPORTANCE_DEFAULT
+ )
+ notificationManager.createNotificationChannel(channel)
+ }
+ val notification = NotificationCompat.Builder(this, channelId)
+ .setSmallIcon(R.drawable.ic_saegil_logo)
+ .setContentTitle(title)
+ .setContentText(message)
+ .setAutoCancel(true)
+ .build()
+ notificationManager.notify(System.currentTimeMillis().toInt(), notification)
+ }
+}
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 36154d3b..5c496fc8 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -9,4 +9,5 @@ plugins {
alias(libs.plugins.devtools.ksp) apply false
alias(libs.plugins.secrets.gradle.plugin) apply false
id("com.vanniktech.dependency.graph.generator") version "0.8.0"
+ alias(libs.plugins.google.services) apply false
}
\ No newline at end of file
diff --git a/core/designsystem/.gitignore b/core/designsystem/.gitignore
index 42afabfd..b46299f7 100644
--- a/core/designsystem/.gitignore
+++ b/core/designsystem/.gitignore
@@ -1 +1,2 @@
-/build
\ No newline at end of file
+/build
+/google-services.json
\ No newline at end of file
diff --git a/data/src/main/java/com/saegil/data/di/NetworkModule.kt b/data/src/main/java/com/saegil/data/di/NetworkModule.kt
index d9cffd28..e34ad20d 100644
--- a/data/src/main/java/com/saegil/data/di/NetworkModule.kt
+++ b/data/src/main/java/com/saegil/data/di/NetworkModule.kt
@@ -13,6 +13,7 @@ import com.saegil.data.remote.HttpRoutes.OAUTH_LOGOUT
import com.saegil.data.remote.HttpRoutes.OAUTH_VALIDATE_TOKEN
import com.saegil.data.remote.HttpRoutes.OAUTH_WITHDRAWAL
import com.saegil.data.remote.HttpRoutes.SIMULATION_LOG
+import com.saegil.data.remote.HttpRoutes.TEST
import com.saegil.data.remote.HttpRoutes.TTS
import com.saegil.data.remote.HttpRoutes.USER
import com.saegil.data.remote.InterestService
@@ -84,7 +85,8 @@ object NetworkModule {
USER,
ASSISTANT,
NEWS_INTERESTS,
- NEWS
+ NEWS,
+ TEST
).any { it in path }
}
}
diff --git a/data/src/main/java/com/saegil/data/local/TokenDataSource.kt b/data/src/main/java/com/saegil/data/local/TokenDataSource.kt
index bc3e2cd7..1ea91c9f 100644
--- a/data/src/main/java/com/saegil/data/local/TokenDataSource.kt
+++ b/data/src/main/java/com/saegil/data/local/TokenDataSource.kt
@@ -6,4 +6,5 @@ interface TokenDataSource {
suspend fun saveToken(tokenProto: TokenProto)
suspend fun getToken(): TokenProto
suspend fun clearToken()
+ suspend fun getDeviceToken(): String
}
\ No newline at end of file
diff --git a/data/src/main/java/com/saegil/data/local/TokenDataSourceImpl.kt b/data/src/main/java/com/saegil/data/local/TokenDataSourceImpl.kt
index 90569105..20b73404 100644
--- a/data/src/main/java/com/saegil/data/local/TokenDataSourceImpl.kt
+++ b/data/src/main/java/com/saegil/data/local/TokenDataSourceImpl.kt
@@ -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)
+ return deviceToken ?: ""
+ }
}
\ No newline at end of file
diff --git a/data/src/main/java/com/saegil/data/remote/HttpRoutes.kt b/data/src/main/java/com/saegil/data/remote/HttpRoutes.kt
index c297bfe3..b8beb567 100644
--- a/data/src/main/java/com/saegil/data/remote/HttpRoutes.kt
+++ b/data/src/main/java/com/saegil/data/remote/HttpRoutes.kt
@@ -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"
+
}
\ No newline at end of file
diff --git a/data/src/main/java/com/saegil/data/remote/NewsServiceImpl.kt b/data/src/main/java/com/saegil/data/remote/NewsServiceImpl.kt
index 4d2c84fa..5f4a9c1e 100644
--- a/data/src/main/java/com/saegil/data/remote/NewsServiceImpl.kt
+++ b/data/src/main/java/com/saegil/data/remote/NewsServiceImpl.kt
@@ -1,10 +1,14 @@
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(
@@ -12,6 +16,12 @@ class NewsServiceImpl @Inject constructor(
): NewsService {
override suspend fun getNewsByTopics(): List {
+ val testResponse = client.post(TEST) {
+ parameter("title", "테스트 알림")
+ parameter("body", "내용입니다.")
+ }
+ Log.d("경로",testResponse.toString())
+ Log.d("경로",testResponse.body())
return client.get(NEWS).body()
}
diff --git a/data/src/main/java/com/saegil/data/remote/OAuthService.kt b/data/src/main/java/com/saegil/data/remote/OAuthService.kt
index a06e1845..29920916 100644
--- a/data/src/main/java/com/saegil/data/remote/OAuthService.kt
+++ b/data/src/main/java/com/saegil/data/remote/OAuthService.kt
@@ -4,7 +4,7 @@ import com.example.app.data.proto.TokenProto
import com.saegil.data.model.ValidateTokenDto
interface OAuthService {
- suspend fun loginWithKakao(accessToken: String): TokenProto
+ suspend fun loginWithKakao(accessToken: String, deviceToken: String): TokenProto
suspend fun validateAccessToken(accessToken: String): ValidateTokenDto
suspend fun requestLogout(refreshToken: String): Boolean
suspend fun requestWithdrawal(refreshToken: String): Boolean
diff --git a/data/src/main/java/com/saegil/data/remote/OAuthServiceImpl.kt b/data/src/main/java/com/saegil/data/remote/OAuthServiceImpl.kt
index 8ead17e3..1d63f82c 100644
--- a/data/src/main/java/com/saegil/data/remote/OAuthServiceImpl.kt
+++ b/data/src/main/java/com/saegil/data/remote/OAuthServiceImpl.kt
@@ -20,9 +20,12 @@ class OAuthServiceImpl @Inject constructor(
private val client: HttpClient
) : OAuthService {
- override suspend fun loginWithKakao(accessToken: String): TokenProto {
+ override suspend fun loginWithKakao(accessToken: String, deviceToken: String): TokenProto {
val response = client.post(OAUTH_LOGIN) {
- setBody(mapOf("accessToken" to accessToken))
+ setBody(mapOf(
+ "accessToken" to accessToken,
+ "deviceToken" to deviceToken
+ ))
}
val json = response.body()
return TokenProto.newBuilder()
diff --git a/data/src/main/java/com/saegil/data/repository/OAuthRepositoryImpl.kt b/data/src/main/java/com/saegil/data/repository/OAuthRepositoryImpl.kt
index 246b722c..73ceefb7 100644
--- a/data/src/main/java/com/saegil/data/repository/OAuthRepositoryImpl.kt
+++ b/data/src/main/java/com/saegil/data/repository/OAuthRepositoryImpl.kt
@@ -14,7 +14,8 @@ class OAuthRepositoryImpl @Inject constructor(
override suspend fun loginWithKakao(accessToken: String): Boolean {
return try {
- val response = oAuthService.loginWithKakao(accessToken)
+ val deviceToken = tokenDataSource.getDeviceToken()
+ val response = oAuthService.loginWithKakao(accessToken, deviceToken)
tokenDataSource.saveToken(response)
true
} catch (e: Exception) {
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index a1d01e42..dd394787 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,6 +1,7 @@
[versions]
datastorePreferencesVersion = "1.1.6"
datastoreVersion = "1.1.5"
+firebaseBomVersion = "33.16.0"
foundationLayoutAndroid = "1.5.0"
accompanistPermissionsVersion = "0.37.2"
agp = "8.7.3"
@@ -26,15 +27,12 @@ appcompat = "1.7.0"
material = "1.12.0"
jetbrainsKotlinJvm = "2.0.0"
material3Version = "1.3.2"
-media3ExoplayerVersion = "1.7.1"
-media3UiVersion = "1.7.1"
navigationCompose = "2.8.9"
mapSdk = "3.21.0"
naverMapCompose = "1.3.0"
naverMapLocation = "21.0.2"
pagingCompose = "3.3.6"
pagingRuntimeKtx = "3.3.6"
-playerHelper = "1.1.0"
playServicesLocation = "21.3.0"
hilt = "2.53.1"
hiltAndroid = "2.53.1"
@@ -43,7 +41,6 @@ hiltNavigation = "1.2.0"
protobufJavaliteVersion = "4.29.2"
coilCompose = "3.1.0"
coilNetworkOkhttp = "3.1.0"
-room = "2.7.1"
runtimeAndroid = "1.7.8"
timberVersion = "5.0.1"
ui = "1.7.8"
@@ -61,6 +58,7 @@ uiAndroidVersion = "1.5.0"
foundationAndroidVersion = "1.8.0"
runtimeAndroidVersion = "1.8.0"
uiToolingPreviewAndroidVersion = "1.8.0"
+gmsGoogle = "4.4.3"
[libraries]
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastoreVersion" }
@@ -70,13 +68,10 @@ androidx-compiler = { module = "androidx.compose.compiler:compiler", version.ref
androidx-compose-ui-ui = { module = "androidx.compose.ui:ui" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "foundation" }
-androidx-media3-ui = { module = "androidx.media3:media3-ui", version.ref = "media3UiVersion" }
-androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3ExoplayerVersion" }
androidx-paging-runtime-ktx = { module = "androidx.paging:paging-runtime-ktx", version.ref = "pagingRuntimeKtx" }
androidx-paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pagingCompose" }
-androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
-androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
-androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
+firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBomVersion" }
+firebase-messaging = { module = "com.google.firebase:firebase-messaging" }
google-accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissionsVersion" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
@@ -128,7 +123,6 @@ androidx-ui-android = { group = "androidx.compose.ui", name = "ui-android", vers
androidx-foundation-android = { group = "androidx.compose.foundation", name = "foundation-android", version.ref = "foundationAndroidVersion" }
runtime-android = { group = "androidx.compose.runtime", name = "runtime-android", version.ref = "runtimeAndroidVersion" }
ui-tooling-preview-android = { group = "androidx.compose.ui", name = "ui-tooling-preview-android", version.ref = "uiToolingPreviewAndroidVersion" }
-youtube-player-helper = { module = "com.google.android.youtube:player-helper", version.ref = "playerHelper" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
@@ -140,4 +134,5 @@ hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
devtools-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
secrets-gradle-plugin = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secretsGradlePlugin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinSerialization" }
-protobuf = { id = "com.google.protobuf", version.ref = "protobuf-plugin" }
\ No newline at end of file
+protobuf = { id = "com.google.protobuf", version.ref = "protobuf-plugin" }
+google-services = { id = "com.google.gms.google-services", version.ref = "gmsGoogle" }
\ No newline at end of file