Skip to content

Commit dff12dd

Browse files
authored
Merge pull request #90 from YAPP-Github/feature/jaino/#76
#76 : 푸시 알림 구현 및 권한 / 푸시 알림 화면 전환
2 parents 979dcba + 34b54de commit dff12dd

File tree

20 files changed

+323
-24
lines changed

20 files changed

+323
-24
lines changed

app/src/main/kotlin/com/bff/wespot/AppNavGraphs.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ object AppNavGraphs {
128128

129129
private val tabScreenNames = listOf(
130130
"vote/vote_home_screen",
131-
"message/message_screen?isMessageSent={isMessageSent}",
131+
"message/message_screen?isMessageSent={isMessageSent}&type={type}&messageId={messageId}",
132132
)
133133

134134
fun NavDestination.navGraph(): NavGraphSpec {

app/src/main/kotlin/com/bff/wespot/MainActivity.kt

+87-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.bff.wespot
22

3+
import android.Manifest
4+
import android.content.pm.PackageManager
5+
import android.os.Build
36
import android.os.Bundle
47
import androidx.activity.ComponentActivity
58
import androidx.activity.compose.setContent
@@ -32,14 +35,23 @@ import androidx.compose.ui.graphics.painter.Painter
3235
import androidx.compose.ui.res.painterResource
3336
import androidx.compose.ui.res.stringResource
3437
import androidx.compose.ui.unit.dp
38+
import androidx.core.app.ActivityCompat
39+
import androidx.core.content.ContextCompat
3540
import androidx.navigation.NavController
3641
import androidx.navigation.NavGraph.Companion.findStartDestination
3742
import androidx.navigation.compose.rememberNavController
43+
import com.ramcosta.composedestinations.dynamic.within
3844
import com.bff.wespot.designsystem.R
3945
import com.bff.wespot.designsystem.component.header.WSTopBar
4046
import com.bff.wespot.designsystem.theme.StaticTypeScale
4147
import com.bff.wespot.designsystem.theme.WeSpotTheme
4248
import com.bff.wespot.designsystem.theme.WeSpotThemeManager
49+
import com.bff.wespot.message.screen.MessageScreenArgs
50+
import com.bff.wespot.message.screen.destinations.MessageScreenDestination
51+
import com.bff.wespot.message.screen.destinations.ReceiverSelectionScreenDestination
52+
import com.bff.wespot.message.screen.send.ReceiverSelectionScreenArgs
53+
import com.bff.wespot.model.notification.NotificationType
54+
import com.bff.wespot.model.notification.convertNotificationType
4355
import com.bff.wespot.navigation.Navigator
4456
import com.ramcosta.composedestinations.navigation.navigate
4557
import com.ramcosta.composedestinations.spec.NavGraphSpec
@@ -54,18 +66,57 @@ class MainActivity : ComponentActivity() {
5466

5567
override fun onCreate(savedInstanceState: Bundle?) {
5668
super.onCreate(savedInstanceState)
69+
requestNotificationPermission()
5770

5871
setContent {
5972
WeSpotTheme {
60-
MainScreen(navigator)
73+
MainScreen(
74+
navigator = navigator,
75+
navArgs = getMainScreenArgsFromIntent(),
76+
)
77+
}
78+
}
79+
}
80+
81+
private fun requestNotificationPermission() {
82+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
83+
val hasPermission = ContextCompat.checkSelfPermission(
84+
this,
85+
Manifest.permission.POST_NOTIFICATIONS
86+
) == PackageManager.PERMISSION_GRANTED
87+
88+
if(!hasPermission) {
89+
ActivityCompat.requestPermissions(
90+
this,
91+
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
92+
0
93+
)
6194
}
6295
}
6396
}
97+
98+
private fun getMainScreenArgsFromIntent(): MainScreenNavArgs {
99+
val targetId = intent.getStringExtra("targetId")?.toInt() ?: -1
100+
val date = intent.getStringExtra("date") ?: ""
101+
val type = intent.getStringExtra("type") ?: ""
102+
103+
return MainScreenNavArgs(
104+
targetId = targetId,
105+
date = date,
106+
type = convertNotificationType(type),
107+
)
108+
}
64109
}
65110

111+
data class MainScreenNavArgs(
112+
val type: NotificationType,
113+
val date: String,
114+
val targetId: Int,
115+
)
116+
66117
@OptIn(ExperimentalMaterial3Api::class)
67118
@Composable
68-
private fun MainScreen(navigator: Navigator) {
119+
private fun MainScreen(navigator: Navigator, navArgs: MainScreenNavArgs) {
69120
val navController = rememberNavController()
70121

71122
val checkScreen by navController.checkCurrentScreen()
@@ -123,6 +174,8 @@ private fun MainScreen(navigator: Navigator) {
123174
modifier = Modifier.padding(it),
124175
navigator = navigator
125176
)
177+
178+
navigateScreenFromNavArgs(navArgs, navController)
126179
}
127180
}
128181

@@ -235,6 +288,38 @@ private fun TabItem(
235288
}
236289
}
237290

291+
private fun navigateScreenFromNavArgs(navArgs: MainScreenNavArgs, navController: NavController) {
292+
when (navArgs.type) {
293+
NotificationType.MESSAGE -> {
294+
navController.navigate(
295+
ReceiverSelectionScreenDestination(
296+
ReceiverSelectionScreenArgs(false),
297+
) within AppNavGraphs.message
298+
)
299+
}
300+
301+
NotificationType.MESSAGE_SENT, NotificationType.MESSAGE_RECEIVED -> {
302+
navController.navigate(
303+
MessageScreenDestination(
304+
MessageScreenArgs(
305+
type = navArgs.type,
306+
messageId = navArgs.targetId,
307+
),
308+
) within AppNavGraphs.message
309+
)
310+
}
311+
312+
NotificationType.VOTE -> {
313+
}
314+
NotificationType.VOTE_RESULT -> {
315+
}
316+
NotificationType.VOTE_RECEIVED -> {
317+
}
318+
NotificationType.IDLE -> {
319+
}
320+
}
321+
}
322+
238323
private fun NavController.navigateToNavGraph(navGraph: NavGraphSpec) {
239324
this.navigate(navGraph) {
240325
launchSingleTop = true

app/src/main/kotlin/com/bff/wespot/NavigatorImpl.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ class NavigatorImpl @Inject constructor() : Navigator {
1919

2020
override fun navigateToMain(
2121
context: Context,
22+
targetId: Pair<String, Int>,
23+
date: Pair<String, String>,
24+
type: Pair<String, String>,
2225
): Intent {
23-
val intent = context.buildIntent<MainActivity>()
26+
val intent = context.buildIntent<MainActivity>(targetId, date, type)
2427
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
2528
return intent
2629
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,83 @@
11
package com.bff.wespot
22

3+
import android.app.Notification
4+
import android.app.NotificationManager
5+
import android.app.PendingIntent
6+
import android.content.Intent
7+
import android.media.RingtoneManager
8+
import androidx.core.app.NotificationCompat
9+
import com.bff.wespot.auth.AuthActivity
10+
import com.bff.wespot.common.CHANNEL_ID
11+
import com.bff.wespot.domain.repository.DataStoreRepository
12+
import com.bff.wespot.domain.util.DataStoreKey.PUSH_TOKEN
313
import com.google.firebase.messaging.FirebaseMessagingService
414
import com.google.firebase.messaging.RemoteMessage
15+
import kotlinx.coroutines.CoroutineDispatcher
16+
import kotlinx.coroutines.CoroutineScope
17+
import kotlinx.coroutines.cancel
18+
import kotlinx.coroutines.launch
19+
import javax.inject.Inject
520

621
class PushNotificationService : FirebaseMessagingService() {
22+
23+
@Inject
24+
lateinit var dataStore: DataStoreRepository
25+
26+
@Inject
27+
lateinit var coroutineDispatcher: CoroutineDispatcher
28+
private val coroutineScope by lazy { CoroutineScope(coroutineDispatcher) }
29+
730
override fun onNewToken(token: String) {
831
super.onNewToken(token)
32+
coroutineScope.launch {
33+
dataStore.saveString(PUSH_TOKEN, token)
34+
}
935
}
1036

1137
override fun onMessageReceived(message: RemoteMessage) {
1238
super.onMessageReceived(message)
39+
if (message.data.isNotEmpty() || message.notification != null) {
40+
sendNotification(message)
41+
}
42+
}
43+
44+
private fun sendNotification(message: RemoteMessage) {
45+
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
46+
val notificationId: Int = (System.currentTimeMillis()).toInt()
47+
48+
val intent = Intent(this, AuthActivity::class.java)
49+
for (key in message.data.keys) {
50+
intent.putExtra(key, message.data[key])
51+
}
52+
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
53+
54+
val pendingIntent = PendingIntent.getActivity(
55+
this,
56+
notificationId,
57+
intent,
58+
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_MUTABLE
59+
)
60+
61+
val title = message.notification?.title
62+
val content = message.notification?.body
63+
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
64+
65+
val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
66+
// TODO Set AppIcon
67+
.setSmallIcon(R.drawable.ic_launcher_background)
68+
.setContentTitle(title)
69+
.setContentText(content)
70+
.setAutoCancel(true)
71+
.setSound(defaultSoundUri)
72+
.setPriority(NotificationCompat.PRIORITY_HIGH)
73+
.setDefaults(Notification.DEFAULT_ALL)
74+
.setContentIntent(pendingIntent)
75+
76+
notificationManager.notify(notificationId, notificationBuilder.build())
77+
}
78+
79+
override fun onDestroy() {
80+
super.onDestroy()
81+
coroutineScope.cancel()
1382
}
1483
}

app/src/main/kotlin/com/bff/wespot/application/WeSpotApplication.kt

+19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
package com.bff.wespot.application
22

33
import android.app.Application
4+
import android.app.NotificationChannel
5+
import android.app.NotificationManager
6+
import android.os.Build
47
import com.bff.wespot.BuildConfig
8+
import com.bff.wespot.common.CHANNEL_DESCRIPTION
9+
import com.bff.wespot.common.CHANNEL_ID
10+
import com.bff.wespot.common.CHANNEL_NAME
511
import com.kakao.sdk.common.KakaoSdk
612
import dagger.hilt.android.HiltAndroidApp
713
import timber.log.Timber
@@ -12,6 +18,19 @@ class WeSpotApplication : Application() {
1218
super.onCreate()
1319
initialTimber()
1420
initKakaoSdk()
21+
initNotificationChannel()
22+
}
23+
24+
private fun initNotificationChannel() {
25+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
26+
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
27+
val importance = NotificationManager.IMPORTANCE_HIGH
28+
val channel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME, importance).apply {
29+
description = CHANNEL_DESCRIPTION
30+
}
31+
32+
notificationManager.createNotificationChannel(channel)
33+
}
1534
}
1635

1736
private fun initKakaoSdk() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.bff.wespot.common
2+
3+
internal const val CHANNEL_ID = "WeSpot_channel"
4+
internal const val CHANNEL_NAME = "WeSpot_default_channel"
5+
internal const val CHANNEL_DESCRIPTION = "WeSpot_notification_channel"

core/model/src/main/kotlin/com/bff/wespot/model/notification/NotificationType.kt

+11
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,14 @@ enum class NotificationType {
1616
IDLE -> ""
1717
}
1818
}
19+
20+
fun convertNotificationType(type: String): NotificationType =
21+
when (type) {
22+
NotificationType.MESSAGE.name -> NotificationType.MESSAGE
23+
NotificationType.MESSAGE_SENT.name -> NotificationType.MESSAGE
24+
NotificationType.MESSAGE_RECEIVED.name -> NotificationType.MESSAGE
25+
NotificationType.VOTE.name -> NotificationType.MESSAGE
26+
NotificationType.VOTE_RESULT.name -> NotificationType.MESSAGE
27+
NotificationType.VOTE_RECEIVED.name -> NotificationType.MESSAGE
28+
else -> NotificationType.IDLE
29+
}

core/navigation/src/main/kotlin/com/bff/wespot/navigation/Navigator.kt

+6-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ import android.content.Intent
55
import android.net.Uri
66

77
interface Navigator {
8-
9-
fun navigateToMain(context: Context) : Intent
8+
fun navigateToMain(
9+
context: Context,
10+
targetId: Pair<String, Int>,
11+
date: Pair<String, String>,
12+
type: Pair<String, String>,
13+
) : Intent
1014

1115
fun navigateToAuth(context: Context): Intent
1216

Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
package com.bff.wespot.navigation.util
22

3-
const val EXTRA_TOAST_MESSAGE = "extra_toast_message"
3+
const val EXTRA_TOAST_MESSAGE = "extra_toast_message"
4+
const val EXTRA_TARGET_ID = "extra_target_id"
5+
const val EXTRA_DATE= "extra_date"
6+
const val EXTRA_TYPE = "extra_type"

domain/src/main/kotlin/com/bff/wespot/domain/util/DataStoreKey.kt

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ object DataStoreKey {
55
const val REFRESH_TOKEN = "refresh_token"
66
const val REFRESH_TOKEN_EXPIRED_AT = "refresh_token_expired_date"
77
const val SIGN_UP_TOKEN = "signup_token"
8+
const val PUSH_TOKEN = "push_token"
89
const val SETTING_DIALOG = "setting_dialog"
910
const val VOTE_ONBOARDING = "vote_onboarding"
1011
}

feature/auth/src/main/kotlin/com/bff/wespot/auth/AuthActivity.kt

+19-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ import com.bff.wespot.designsystem.component.indicator.WSToastType
3333
import com.bff.wespot.designsystem.theme.WeSpotTheme
3434
import com.bff.wespot.model.constants.LoginState
3535
import com.bff.wespot.navigation.Navigator
36+
import com.bff.wespot.navigation.util.EXTRA_DATE
37+
import com.bff.wespot.navigation.util.EXTRA_TARGET_ID
3638
import com.bff.wespot.navigation.util.EXTRA_TOAST_MESSAGE
39+
import com.bff.wespot.navigation.util.EXTRA_TYPE
3740
import com.ramcosta.composedestinations.DestinationsNavHost
3841
import com.ramcosta.composedestinations.navigation.dependency
3942
import com.ramcosta.composedestinations.navigation.navigate
@@ -98,7 +101,12 @@ class AuthActivity : ComponentActivity() {
98101
}
99102

100103
AuthSideEffect.NavigateToMainActivity -> {
101-
val intent = navigator.navigateToMain(this)
104+
val intent = navigator.navigateToMain(
105+
this,
106+
Pair("", 0),
107+
Pair("", ""),
108+
Pair("", ""),
109+
)
102110
startActivity(intent)
103111
}
104112
}
@@ -150,7 +158,16 @@ class AuthActivity : ComponentActivity() {
150158
override fun onPreDraw(): Boolean {
151159
return if (::loginState.isInitialized) {
152160
if (loginState == LoginState.LOGIN_SUCCESS) {
153-
val intent = navigator.navigateToMain(this@AuthActivity)
161+
val targetId = intent.getStringExtra("targetId")?.toInt() ?: -1
162+
val date = intent.getStringExtra("date") ?: ""
163+
val type = intent.getStringExtra("type") ?: ""
164+
165+
val intent = navigator.navigateToMain(
166+
this@AuthActivity,
167+
targetId = Pair(EXTRA_TARGET_ID, targetId),
168+
date = Pair(EXTRA_DATE, date),
169+
type = Pair(EXTRA_TYPE, type),
170+
)
154171
startActivity(intent)
155172
}
156173
content.viewTreeObserver.removeOnPreDrawListener(this)

0 commit comments

Comments
 (0)