-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] 케어콜 푸시알림 설정 #110 #122
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
Changes from all commits
93f165b
ad4a373
68042f2
9da1e74
dac99e8
7e40411
55374c0
590709d
a771654
4b2d693
e563638
9573e20
d51baa8
49cd59c
274adbc
1600115
7578a24
e9dd560
3cfa948
9680847
cf3449e
bba1831
4bedbef
37a63b7
60fb502
6b1f2d2
08129f8
0ca4014
26d6828
ff4a6b0
f7587db
32b4b43
169ac56
0711e97
2e00557
b75e47e
d05c31d
a7908f7
0dbc2c3
4c88cd9
36b7883
e840883
c8dec6a
f94906b
4701d51
61421e1
a7e8067
6ffed96
9f43a53
a37f941
d9e79af
4bb9ad1
66cd9e9
86c0f71
48ab848
cad0504
1ff751f
f3a5b46
fabaa14
495b2a6
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 |
|---|---|---|
|
|
@@ -8,7 +8,7 @@ plugins { | |
| alias(libs.plugins.ksp) | ||
| kotlin("plugin.serialization") version "2.0.21" | ||
| alias(libs.plugins.detekt) | ||
|
|
||
| alias(libs.plugins.google.services) | ||
| } | ||
|
|
||
| detekt { | ||
|
|
@@ -21,11 +21,6 @@ android { | |
| namespace = "com.konkuk.medicarecall" | ||
| compileSdk = 36 | ||
|
|
||
| buildFeatures { | ||
| buildConfig = true | ||
| } | ||
|
|
||
|
|
||
| defaultConfig { | ||
| applicationId = "com.konkuk.medicarecall" | ||
| minSdk = 26 | ||
|
|
@@ -39,50 +34,58 @@ android { | |
| load(project.rootProject.file("local.properties").inputStream()) | ||
| } | ||
|
|
||
| val baseUrl = properties["base.url"]?.toString()?.let { "\"$it\"" } ?: "\"\"" | ||
| buildConfigField("String", "BASE_URL", baseUrl) | ||
| buildConfigField("String", "BASE_URL", "\"${properties["base.url"] ?: ""}\"") | ||
| } | ||
|
|
||
| buildTypes { | ||
| release { | ||
| isMinifyEnabled = false | ||
| proguardFiles( | ||
| getDefaultProguardFile("proguard-android-optimize.txt"), | ||
| "proguard-rules.pro" | ||
| "proguard-rules.pro", | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| compileOptions { | ||
| sourceCompatibility = JavaVersion.VERSION_11 | ||
| targetCompatibility = JavaVersion.VERSION_11 | ||
|
|
||
| } | ||
|
|
||
| kotlinOptions { | ||
| jvmTarget = "11" | ||
| } | ||
|
|
||
| buildFeatures { | ||
| compose = true | ||
| buildConfig = true | ||
| } | ||
|
|
||
| } | ||
|
|
||
| dependencies { | ||
|
|
||
|
|
||
| // AndroidX & Compose | ||
| implementation(libs.androidx.core.ktx) | ||
| implementation(libs.androidx.lifecycle.runtime.ktx) | ||
| implementation(libs.androidx.activity.compose) | ||
| implementation(platform(libs.androidx.compose.bom)) | ||
| implementation(libs.androidx.ui) | ||
| implementation(libs.androidx.ui.graphics) | ||
| implementation(libs.androidx.ui.tooling.preview) | ||
| implementation(libs.androidx.foundation) | ||
| implementation(libs.androidx.material3) | ||
| implementation(libs.androidx.navigation.runtime.android) | ||
| implementation(libs.androidx.navigation.compose) | ||
| implementation(libs.androidx.compose.material3) | ||
| implementation(libs.androidx.datastore) | ||
| implementation(libs.androidx.core.splashscreen) | ||
| implementation(libs.androidx.media3.common.ktx) | ||
| implementation(libs.play.services.vision.common) | ||
|
|
||
| // DataStore (명시적으로 추가) | ||
| implementation("androidx.datastore:datastore-preferences:1.1.1") | ||
| implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") | ||
| implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1") | ||
|
|
||
| // Test | ||
| testImplementation(libs.junit) | ||
| androidTestImplementation(libs.androidx.junit) | ||
| androidTestImplementation(libs.androidx.espresso.core) | ||
|
|
@@ -91,32 +94,31 @@ dependencies { | |
| debugImplementation(libs.androidx.ui.tooling) | ||
| debugImplementation(libs.androidx.ui.test.manifest) | ||
|
|
||
|
|
||
|
|
||
| implementation(libs.androidx.foundation) | ||
| // kotlin serialization | ||
| // Kotlin Serialization | ||
| implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0") | ||
|
|
||
| // Retrofit | ||
| // Retrofit & OkHttp | ||
| implementation("com.squareup.retrofit2:retrofit:2.12.0") | ||
| implementation("com.squareup.retrofit2:converter-kotlinx-serialization:2.12.0") | ||
|
|
||
| // OkHttp | ||
| implementation("com.squareup.okhttp3:okhttp:4.10.0") | ||
| implementation("com.squareup.okhttp3:logging-interceptor:4.10.0") | ||
| implementation("com.squareup.retrofit2:converter-gson:2.9.0") | ||
|
|
||
| // WebView | ||
| implementation("com.google.accompanist:accompanist-webview:0.24.13-rc") | ||
| detektPlugins(libs.detekt.formatting) | ||
|
|
||
|
|
||
| // Hilt | ||
| implementation(libs.hilt.android) | ||
| implementation(libs.hilt.core) | ||
| implementation(libs.hilt.navigation.compose) | ||
| ksp(libs.hilt.android.compiler) | ||
| ksp(libs.hilt.compiler) | ||
|
|
||
| // Firebase | ||
| implementation(platform(libs.firebase.bom)) | ||
| implementation(libs.google.firebase.analytics) | ||
| implementation(libs.firebase.messaging) | ||
|
|
||
| // Detekt formatting plugin | ||
| detektPlugins(libs.detekt.formatting) | ||
| ksp(libs.hilt.manager) | ||
| } | ||
|
Comment on lines
122
to
123
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. 중요:
적용 diff(불필요 시 제거): - ksp(libs.hilt.manager)만약 WorkManager 등의 AndroidX Hilt 확장에 컴파일러가 필요하면 다음처럼 전환: // plugins 블록에 추가
plugins {
id("org.jetbrains.kotlin.kapt")
}
// dependencies 블록
kapt(libs.hilt.manager)또는 해당 확장을 쓰지 않는다면 toml의 🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,95 @@ | ||
| package com.konkuk.medicarecall | ||
|
|
||
| import dagger.hilt.android.HiltAndroidApp | ||
| import android.app.Application | ||
| import android.app.Notification | ||
| import android.app.NotificationChannel | ||
| import android.app.NotificationManager | ||
| import android.os.Build | ||
| import android.util.Log | ||
| import com.google.firebase.messaging.FirebaseMessaging | ||
| import com.konkuk.medicarecall.data.repository.FcmRepository | ||
| import dagger.hilt.android.HiltAndroidApp | ||
| import kotlinx.coroutines.CoroutineScope | ||
| import kotlinx.coroutines.Dispatchers | ||
| import kotlinx.coroutines.SupervisorJob | ||
| import kotlinx.coroutines.launch | ||
| import javax.inject.Inject | ||
|
|
||
| @HiltAndroidApp | ||
| class App : Application() { | ||
| @Inject | ||
| lateinit var fcmRepository: FcmRepository | ||
|
|
||
| // Application 전체에서 쑬 수 있는 스코프(앱이 살아있는 동안 유지돼야 하는 초기화/저장 작업 진행 - 여러 초기화 작업 한덩어리로 관리) | ||
| private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) | ||
|
|
||
| override fun onCreate() { | ||
| super.onCreate() | ||
| createNotificationChannel() | ||
| fetchAndStoreFcmToken() | ||
| } | ||
|
|
||
| // Android 8.0 이상에서 FCM 알림 표시하기 위한 채널 생성 | ||
| private fun createNotificationChannel() { | ||
| if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return | ||
| val nm = getSystemService(NotificationManager::class.java) ?: return | ||
|
|
||
| val channel = NotificationChannel( | ||
| FCM_CHANNEL_ID, | ||
| "FCM 알림", | ||
| NotificationManager.IMPORTANCE_HIGH, | ||
| ).apply { | ||
| description = "Firebase Cloud Messaging으로부터 수신된 알림을 표시합니다." | ||
| enableVibration(true) | ||
| vibrationPattern = longArrayOf(0, 250, 150, 250) | ||
| setShowBadge(true) | ||
| lockscreenVisibility = Notification.VISIBILITY_PUBLIC | ||
| } | ||
|
|
||
| nm.createNotificationChannel(channel) | ||
| } | ||
|
|
||
| /** | ||
| * 앱 시작 시점에 한 번 FCM 토큰을 가져와서 DataStore에 저장 | ||
| * (실제로 토큰이 바뀌면 FirebaseMessagingService.onNewToken(...)에서도 다시 저장해야 함) | ||
| */ | ||
| private fun fetchAndStoreFcmToken() { | ||
| FirebaseMessaging.getInstance().token | ||
| .addOnCompleteListener { task -> | ||
| if (!task.isSuccessful) { | ||
| if (BuildConfig.DEBUG) { | ||
| Log.w(TAG, "토큰 가져오기 실패", task.exception) | ||
| } | ||
| return@addOnCompleteListener | ||
| } | ||
|
|
||
| val token = task.result | ||
| if (token.isNullOrBlank()) { | ||
| if (BuildConfig.DEBUG) { | ||
| Log.w(TAG, "FCM Token is empty") | ||
| } | ||
| return@addOnCompleteListener | ||
| } | ||
| if (BuildConfig.DEBUG) { | ||
| Log.d(TAG, "FCM token(full)=$token") | ||
| } | ||
|
|
||
| // FCM 토큰을 DataStore(AppPreferences)에 저장 | ||
| appScope.launch { | ||
| try { | ||
| fcmRepository.saveFcmToken(token) | ||
| if (BuildConfig.DEBUG) { | ||
| Log.d(TAG, "FCM token saved to DataStore") | ||
| } | ||
| } catch (e: Exception) { | ||
| Log.e(TAG, "Failed to save FCM token", e) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| companion object { | ||
| private const val TAG = "FCM" | ||
| const val FCM_CHANNEL_ID = "fcm_alert" | ||
| } | ||
| } |
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.
포크 PR에서 시크릿 미제공 시 빌드 깨짐 위험 — 시크릿 의존 스텝 가드 필요
fork에서 오는 pull_request 이벤트엔 시크릿이 전달되지 않아 google-services.json/local.properties 생성 스텝이 빈 파일을 만들거나 실패 → 빌드 실패로 이어질 수 있습니다. 아래처럼 시크릿 존재 여부로 가드하세요.
추천 변경(해당 스텝 라인에서 if 추가):
옵션(포크 PR용 스텁 파일 생성 — 시크릿 없을 때만):
옵션(중복 러너 낭비 방지):
🤖 Prompt for AI Agents