Skip to content

Commit 527a730

Browse files
committed
Merge branch 'develop' into feature/jaino/#63
2 parents cd0e1c3 + e8ae3a1 commit 527a730

File tree

56 files changed

+591
-189
lines changed

Some content is hidden

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

56 files changed

+591
-189
lines changed

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

+20-12
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@ import androidx.navigation.NavGraph
1515
import androidx.navigation.NavHostController
1616
import com.bff.wespot.entire.screen.screen.destinations.AccountSettingScreenDestination
1717
import com.bff.wespot.entire.screen.screen.destinations.BlockListScreenDestination
18-
import com.bff.wespot.entire.screen.screen.destinations.CharacterEditScreenDestination
1918
import com.bff.wespot.entire.screen.screen.destinations.EntireScreenDestination
2019
import com.bff.wespot.entire.screen.screen.destinations.NotificationSettingScreenDestination
21-
import com.bff.wespot.entire.screen.screen.destinations.ProfileEditScreenDestination
2220
import com.bff.wespot.entire.screen.screen.destinations.RevokeConfirmScreenDestination
2321
import com.bff.wespot.entire.screen.screen.destinations.RevokeScreenDestination
2422
import com.bff.wespot.entire.screen.screen.destinations.SettingScreenDestination
23+
import com.bff.wespot.entire.screen.screen.destinations.ProfileEditScreenDestination
24+
import com.bff.wespot.entire.screen.screen.destinations.CharacterEditScreenDestination
2525
import com.bff.wespot.message.screen.destinations.MessageEditScreenDestination
2626
import com.bff.wespot.message.screen.destinations.MessageScreenDestination
2727
import com.bff.wespot.message.screen.destinations.MessageWriteScreenDestination
2828
import com.bff.wespot.message.screen.destinations.ReceiverSelectionScreenDestination
2929
import com.bff.wespot.message.screen.destinations.ReservedMessageScreenDestination
3030
import com.bff.wespot.message.viewmodel.SendViewModel
31+
import com.bff.wespot.model.ToastState
3132
import com.bff.wespot.notification.screen.destinations.NotificationScreenDestination
3233
import com.bff.wespot.navigation.Navigator
3334
import com.bff.wespot.vote.screen.destinations.CharacterSettingScreenDestination
@@ -126,9 +127,15 @@ object AppNavGraphs {
126127
}
127128
}
128129

129-
private val tabScreenNames = listOf(
130+
private val bottomBarScreenNames = listOf(
130131
"vote/vote_home_screen",
131132
"message/message_screen?isMessageSent={isMessageSent}",
133+
"entire/entire_screen",
134+
)
135+
136+
private val topBarScreenNames = listOf(
137+
"vote/vote_home_screen",
138+
"message/message_screen?isMessageSent={isMessageSent}&type={type}&messageId={messageId}",
132139
)
133140

134141
fun NavDestination.navGraph(): NavGraphSpec {
@@ -143,15 +150,15 @@ fun NavDestination.navGraph(): NavGraphSpec {
143150
throw ClassNotFoundException("Unknown nav graph for destination $route")
144151
}
145152

146-
fun NavDestination.checkDestination(): Boolean {
147-
hierarchy.forEach { destination ->
148-
tabScreenNames.forEach { name ->
149-
if (destination.route == name) {
150-
return true
151-
}
152-
}
153+
internal fun NavDestination.checkDestination(position: NavigationBarPosition): Boolean {
154+
val screenNames = when (position) {
155+
NavigationBarPosition.BOTTOM -> bottomBarScreenNames
156+
NavigationBarPosition.TOP -> topBarScreenNames
157+
}
158+
159+
return hierarchy.any { destination ->
160+
screenNames.any { name -> destination.route == name }
153161
}
154-
return false
155162
}
156163

157164
fun DestinationScopeWithNoDependencies<*>.currentNavigator(): CommonNavGraphNavigator {
@@ -165,6 +172,7 @@ fun DestinationScopeWithNoDependencies<*>.currentNavigator(): CommonNavGraphNavi
165172
internal fun AppNavigation(
166173
navController: NavHostController,
167174
modifier: Modifier = Modifier,
175+
showToast: (ToastState) -> Unit,
168176
navigator: Navigator,
169177
) {
170178
val engine = rememberNavHostEngine(
@@ -187,7 +195,7 @@ internal fun AppNavigation(
187195
dependency(navigator)
188196
dependency(sendViewModel)
189197
dependency(votingViewModel)
190-
dependency(navigator)
198+
dependency(showToast)
191199
},
192200
)
193201
}

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

+112-8
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
@@ -26,20 +29,32 @@ import androidx.compose.runtime.State
2629
import androidx.compose.runtime.getValue
2730
import androidx.compose.runtime.mutableStateOf
2831
import androidx.compose.runtime.remember
32+
import androidx.compose.runtime.setValue
2933
import androidx.compose.ui.Alignment
3034
import androidx.compose.ui.Modifier
3135
import androidx.compose.ui.graphics.painter.Painter
3236
import androidx.compose.ui.res.painterResource
3337
import androidx.compose.ui.res.stringResource
3438
import androidx.compose.ui.unit.dp
39+
import androidx.core.app.ActivityCompat
40+
import androidx.core.content.ContextCompat
3541
import androidx.navigation.NavController
3642
import androidx.navigation.NavGraph.Companion.findStartDestination
3743
import androidx.navigation.compose.rememberNavController
44+
import com.ramcosta.composedestinations.dynamic.within
3845
import com.bff.wespot.designsystem.R
3946
import com.bff.wespot.designsystem.component.header.WSTopBar
47+
import com.bff.wespot.designsystem.component.indicator.WSToast
4048
import com.bff.wespot.designsystem.theme.StaticTypeScale
4149
import com.bff.wespot.designsystem.theme.WeSpotTheme
4250
import com.bff.wespot.designsystem.theme.WeSpotThemeManager
51+
import com.bff.wespot.model.ToastState
52+
import com.bff.wespot.message.screen.MessageScreenArgs
53+
import com.bff.wespot.message.screen.destinations.MessageScreenDestination
54+
import com.bff.wespot.message.screen.destinations.ReceiverSelectionScreenDestination
55+
import com.bff.wespot.message.screen.send.ReceiverSelectionScreenArgs
56+
import com.bff.wespot.model.notification.NotificationType
57+
import com.bff.wespot.model.notification.convertNotificationType
4358
import com.bff.wespot.navigation.Navigator
4459
import com.ramcosta.composedestinations.navigation.navigate
4560
import com.ramcosta.composedestinations.spec.NavGraphSpec
@@ -54,26 +69,67 @@ class MainActivity : ComponentActivity() {
5469

5570
override fun onCreate(savedInstanceState: Bundle?) {
5671
super.onCreate(savedInstanceState)
72+
requestNotificationPermission()
5773

5874
setContent {
5975
WeSpotTheme {
60-
MainScreen(navigator)
76+
MainScreen(
77+
navigator = navigator,
78+
navArgs = getMainScreenArgsFromIntent(),
79+
)
80+
}
81+
}
82+
}
83+
84+
private fun requestNotificationPermission() {
85+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
86+
val hasPermission = ContextCompat.checkSelfPermission(
87+
this,
88+
Manifest.permission.POST_NOTIFICATIONS
89+
) == PackageManager.PERMISSION_GRANTED
90+
91+
if(!hasPermission) {
92+
ActivityCompat.requestPermissions(
93+
this,
94+
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
95+
0
96+
)
6197
}
6298
}
6399
}
100+
101+
private fun getMainScreenArgsFromIntent(): MainScreenNavArgs {
102+
val targetId = intent.getStringExtra("targetId")?.toInt() ?: -1
103+
val date = intent.getStringExtra("date") ?: ""
104+
val type = intent.getStringExtra("type") ?: ""
105+
106+
return MainScreenNavArgs(
107+
targetId = targetId,
108+
date = date,
109+
type = convertNotificationType(type),
110+
)
111+
}
64112
}
65113

114+
data class MainScreenNavArgs(
115+
val type: NotificationType,
116+
val date: String,
117+
val targetId: Int,
118+
)
119+
66120
@OptIn(ExperimentalMaterial3Api::class)
67121
@Composable
68-
private fun MainScreen(navigator: Navigator) {
122+
private fun MainScreen(navigator: Navigator, navArgs: MainScreenNavArgs) {
69123
val navController = rememberNavController()
124+
var toast by remember { mutableStateOf(ToastState()) }
70125

71-
val checkScreen by navController.checkCurrentScreen()
126+
val isTopNavigationScreen by navController.checkCurrentScreen(NavigationBarPosition.TOP)
127+
val isBottomNavigationScreen by navController.checkCurrentScreen(NavigationBarPosition.BOTTOM)
72128

73129
Scaffold(
74130
modifier = Modifier.fillMaxSize(),
75131
topBar = {
76-
if (checkScreen) {
132+
if (isTopNavigationScreen) {
77133
WSTopBar(
78134
title = "",
79135
navigation = {
@@ -106,7 +162,7 @@ private fun MainScreen(navigator: Navigator) {
106162
}
107163
},
108164
bottomBar = {
109-
if (checkScreen) {
165+
if (isBottomNavigationScreen) {
110166
val currentSelectedItem by navController.currentScreenAsState()
111167
BottomNavigationTab(
112168
selectedNavigation = currentSelectedItem,
@@ -121,8 +177,24 @@ private fun MainScreen(navigator: Navigator) {
121177
AppNavigation(
122178
navController = navController,
123179
modifier = Modifier.padding(it),
124-
navigator = navigator
180+
navigator = navigator,
181+
showToast = { toastState -> toast = toastState }
125182
)
183+
184+
navigateScreenFromNavArgs(navArgs, navController)
185+
}
186+
187+
if (toast.show) {
188+
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) {
189+
WSToast(
190+
text = stringResource(toast.message),
191+
showToast = toast.show,
192+
toastType = toast.type,
193+
closeToast = {
194+
toast = toast.copy(show = false)
195+
},
196+
)
197+
}
126198
}
127199
}
128200

@@ -177,12 +249,12 @@ private fun NavController.currentScreenAsState(): State<NavGraphSpec> {
177249

178250
@Stable
179251
@Composable
180-
private fun NavController.checkCurrentScreen(): State<Boolean> {
252+
private fun NavController.checkCurrentScreen(position: NavigationBarPosition): State<Boolean> {
181253
val showBar = remember { mutableStateOf(false) }
182254

183255
DisposableEffect(this) {
184256
val listener = NavController.OnDestinationChangedListener { _, destination, _ ->
185-
showBar.value = destination.checkDestination()
257+
showBar.value = destination.checkDestination(position)
186258
}
187259

188260
addOnDestinationChangedListener(listener)
@@ -235,6 +307,38 @@ private fun TabItem(
235307
}
236308
}
237309

310+
private fun navigateScreenFromNavArgs(navArgs: MainScreenNavArgs, navController: NavController) {
311+
when (navArgs.type) {
312+
NotificationType.MESSAGE -> {
313+
navController.navigate(
314+
ReceiverSelectionScreenDestination(
315+
ReceiverSelectionScreenArgs(false),
316+
) within AppNavGraphs.message
317+
)
318+
}
319+
320+
NotificationType.MESSAGE_SENT, NotificationType.MESSAGE_RECEIVED -> {
321+
navController.navigate(
322+
MessageScreenDestination(
323+
MessageScreenArgs(
324+
type = navArgs.type,
325+
messageId = navArgs.targetId,
326+
),
327+
) within AppNavGraphs.message
328+
)
329+
}
330+
331+
NotificationType.VOTE -> {
332+
}
333+
NotificationType.VOTE_RESULT -> {
334+
}
335+
NotificationType.VOTE_RECEIVED -> {
336+
}
337+
NotificationType.IDLE -> {
338+
}
339+
}
340+
}
341+
238342
private fun NavController.navigateToNavGraph(navGraph: NavGraphSpec) {
239343
this.navigate(navGraph) {
240344
launchSingleTop = true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.bff.wespot
2+
3+
internal enum class NavigationBarPosition {
4+
BOTTOM, TOP,
5+
}

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
}

0 commit comments

Comments
 (0)