Skip to content

Commit de9b3f0

Browse files
committed
Merge remote-tracking branch 'origin/develop' into BOOK-381-feature/#199
2 parents 9e507ff + c846285 commit de9b3f0

45 files changed

Lines changed: 506 additions & 59 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/src/main/AndroidManifest.xml

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<uses-permission android:name="android.permission.INTERNET" />
1010
<uses-permission android:name="android.permission.CAMERA" />
11-
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
11+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
1212

1313
<application
1414
android:name=".BooketApplication"
@@ -41,6 +41,10 @@
4141
android:name="com.ninecraft.booket.initializer.FirebaseCrashlyticsInitializer"
4242
android:value="androidx.startup" />
4343

44+
<meta-data
45+
android:name="com.ninecraft.booket.initializer.NotificationChannelInitializer"
46+
android:value="androidx.startup" />
47+
4448
</provider>
4549

4650
<activity
@@ -73,6 +77,19 @@
7377
tools:replace="android:resource" />
7478
</provider>
7579

80+
<service
81+
android:name=".ReedFirebaseMessagingService"
82+
android:exported="false">
83+
<intent-filter>
84+
<action android:name="com.google.firebase.MESSAGING_EVENT" />
85+
</intent-filter>
86+
</service>
87+
88+
<meta-data
89+
android:name="com.google.firebase.messaging.default_notification_icon"
90+
android:resource="@drawable/ic_notification" />
91+
<meta-data
92+
android:name="com.google.firebase.messaging.default_notification_color"
93+
android:resource="@color/green_500" />
7694
</application>
77-
7895
</manifest>
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.ninecraft.booket
2+
3+
import android.app.NotificationChannel
4+
import android.app.NotificationManager
5+
import android.app.PendingIntent
6+
import android.content.Context
7+
import android.content.Intent
8+
import androidx.core.app.NotificationCompat
9+
import androidx.core.content.ContextCompat
10+
import com.google.firebase.messaging.FirebaseMessagingService
11+
import com.google.firebase.messaging.RemoteMessage
12+
import com.ninecraft.booket.core.data.api.repository.UserRepository
13+
import com.ninecraft.booket.core.designsystem.R
14+
import com.ninecraft.booket.feature.main.MainActivity
15+
import dagger.hilt.android.AndroidEntryPoint
16+
import kotlinx.coroutines.CoroutineScope
17+
import kotlinx.coroutines.Dispatchers
18+
import kotlinx.coroutines.SupervisorJob
19+
import kotlinx.coroutines.cancel
20+
import kotlinx.coroutines.launch
21+
import javax.inject.Inject
22+
23+
@AndroidEntryPoint
24+
class ReedFirebaseMessagingService : FirebaseMessagingService() {
25+
26+
@Inject
27+
lateinit var userRepository: UserRepository
28+
29+
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
30+
31+
override fun onNewToken(token: String) {
32+
super.onNewToken(token)
33+
34+
scope.launch {
35+
userRepository.syncFcmToken(token)
36+
}
37+
}
38+
39+
override fun onMessageReceived(message: RemoteMessage) {
40+
super.onMessageReceived(message)
41+
42+
val title = message.notification?.title ?: "Reed"
43+
val body = message.notification?.body ?: ""
44+
45+
val intent = Intent(this, MainActivity::class.java).apply {
46+
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
47+
}
48+
49+
val pendingIntent = PendingIntent.getActivity(
50+
this,
51+
0,
52+
intent,
53+
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
54+
)
55+
56+
val builder = NotificationCompat.Builder(this, REED_CHANNEL_ID)
57+
.setSmallIcon(R.drawable.ic_notification)
58+
.setColor(ContextCompat.getColor(this, R.color.green_500))
59+
.setContentTitle(title)
60+
.setContentText(body)
61+
.setContentIntent(pendingIntent)
62+
.setAutoCancel(true)
63+
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
64+
65+
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
66+
manager.notify(System.currentTimeMillis().toInt(), builder.build())
67+
}
68+
69+
override fun onDestroy() {
70+
scope.cancel()
71+
super.onDestroy()
72+
}
73+
74+
companion object {
75+
private const val REED_CHANNEL_ID = "REED_PUSH_CHANNEL"
76+
private const val REED_CHANNEL_NAME = "리드 푸시 알림"
77+
private const val REED_CHANNEL_DESC = "리드 앱에서 보내는 푸시 알림을 관리합니다."
78+
79+
// Android 8.0 이상 필수 채널 생성
80+
fun createNotificationChannel(context: Context) {
81+
val channel = NotificationChannel(
82+
REED_CHANNEL_ID,
83+
REED_CHANNEL_NAME,
84+
NotificationManager.IMPORTANCE_DEFAULT,
85+
).apply {
86+
description = REED_CHANNEL_DESC
87+
}
88+
89+
val manager = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
90+
manager.createNotificationChannel(channel)
91+
}
92+
}
93+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.ninecraft.booket.initializer
2+
3+
import android.content.Context
4+
import androidx.startup.Initializer
5+
import com.ninecraft.booket.ReedFirebaseMessagingService.Companion.createNotificationChannel
6+
7+
class NotificationChannelInitializer : Initializer<Unit> {
8+
9+
override fun create(context: Context) {
10+
createNotificationChannel(context)
11+
}
12+
13+
override fun dependencies(): List<Class<out Initializer<*>>> {
14+
return emptyList()
15+
}
16+
}

build-logic/src/main/kotlin/AndroidFirebaseConventionPlugin.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ internal class AndroidFirebaseConventionPlugin : Plugin<Project> {
1919
implementation(platform(libs.firebase.bom))
2020
implementation(libs.firebase.analytics)
2121
implementation(libs.firebase.crashlytics)
22+
implementation(libs.firebase.messaging)
2223
}
2324
}
2425
}

core/common/src/main/kotlin/com/ninecraft/booket/core/common/extensions/Context.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import androidx.core.content.FileProvider
1616
import com.orhanobut.logger.Logger
1717
import java.io.File
1818

19-
@Suppress("TooGenericExceptionCaught")
2019
fun Context.externalShareForBitmap(bitmap: ImageBitmap) {
2120
try {
2221
val file = File(bitmap.saveToDisk(this))
@@ -31,7 +30,6 @@ fun Context.externalShareForBitmap(bitmap: ImageBitmap) {
3130
}
3231
}
3332

34-
@Suppress("TooGenericExceptionCaught")
3533
fun Context.saveImageToGallery(bitmap: ImageBitmap) {
3634
try {
3735
val fileName = "reed_record_${System.currentTimeMillis()}.png"

core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/HandleException.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ fun postErrorDialog(
9393
ErrorEventHelper.sendError(event = ErrorEvent.ShowDialog(spec))
9494
}
9595

96-
@Suppress("TooGenericExceptionCaught")
9796
private fun HttpException.parseErrorMessage(): String? {
9897
return try {
9998
val errorBody = response()?.errorBody()?.string()

core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/RunSuspendCatching.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import kotlin.contracts.ExperimentalContracts
1414
*/
1515

1616
@OptIn(ExperimentalContracts::class)
17-
@Suppress("WRONG_INVOCATION_KIND", "TooGenericExceptionCaught")
17+
@Suppress("WRONG_INVOCATION_KIND")
1818
inline fun <T> runSuspendCatching(block: () -> T): Result<T> {
1919
// 계약(contract): 컴파일러에게 'block' 람다의 실행 시점과 횟수를 명시적으로 알림
2020
// 'callsInPlace'와 'EXACTLY_ONCE'는 'block'이 이 함수 내에서 즉시, 그리고 정확히 한 번만 실행됨을 보장

core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/UserRepository.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,19 @@ interface UserRepository {
1414

1515
suspend fun setOnboardingCompleted(isCompleted: Boolean)
1616

17-
val isNotificationEnabled: Flow<Boolean>
17+
suspend fun syncFcmToken(): Result<Unit>
1818

19-
suspend fun setNotificationEnabled(isEnabled: Boolean)
19+
suspend fun syncFcmToken(fcmToken: String): Result<Unit>
20+
21+
val isUserNotificationEnabled: Flow<Boolean>
22+
23+
suspend fun getUserNotificationEnabled(): Boolean
24+
25+
suspend fun setUserNotificationEnabled(isEnabled: Boolean)
26+
27+
suspend fun getLastSyncedNotificationEnabled(): Boolean?
28+
29+
suspend fun setLastNotificationSyncedEnabled(isEnabled: Boolean)
30+
31+
suspend fun updateNotificationSettings(notificationEnabled: Boolean): Result<UserProfileModel>
2032
}

core/data/impl/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ dependencies {
2828

2929
platform(libs.firebase.bom),
3030
libs.firebase.remote.config,
31+
libs.firebase.messaging,
3132
libs.logger,
3233
)
3334
}

core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/di/FirebaseModule.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.ninecraft.booket.core.data.impl.di
22

33
import com.google.firebase.Firebase
4+
import com.google.firebase.messaging.FirebaseMessaging
5+
import com.google.firebase.messaging.messaging
46
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
57
import com.google.firebase.remoteconfig.remoteConfig
68
import com.google.firebase.remoteconfig.remoteConfigSettings
@@ -26,4 +28,8 @@ internal object FirebaseModule {
2628
setConfigSettingsAsync(configSettings)
2729
}
2830
}
31+
32+
@Singleton
33+
@Provides
34+
fun provideFirebaseMessaging(): FirebaseMessaging = Firebase.messaging
2935
}

0 commit comments

Comments
 (0)