You've already forked RekomenciMobile
fcm token sending
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
package com.prodhack.moscow2025.data.dto
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class FcmTokenDTO(
|
||||||
|
@SerialName("device_id")
|
||||||
|
val deviceId: String
|
||||||
|
)
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package com.prodhack.moscow2025.data.repImplementations
|
||||||
|
|
||||||
|
import com.prodhack.moscow2025.data.base.BaseRepository
|
||||||
|
import com.prodhack.moscow2025.data.data_providers.api.ApiKtorClient
|
||||||
|
import com.prodhack.moscow2025.data.dto.FcmTokenDTO
|
||||||
|
import com.prodhack.moscow2025.domain.interfaces.FCMRepository
|
||||||
|
import io.ktor.client.request.setBody
|
||||||
|
import io.ktor.client.request.url
|
||||||
|
import io.ktor.http.ContentType
|
||||||
|
import io.ktor.http.HttpMethod
|
||||||
|
import io.ktor.http.contentType
|
||||||
|
import org.koin.core.annotation.Single
|
||||||
|
|
||||||
|
@Single
|
||||||
|
class FCMRepositoryImpl(
|
||||||
|
val ktorClient: ApiKtorClient
|
||||||
|
) : FCMRepository, BaseRepository() {
|
||||||
|
|
||||||
|
override val defaultKtorClient = ktorClient.client
|
||||||
|
|
||||||
|
override suspend fun sendFCMToken(token: String) {
|
||||||
|
networkRequest<String> {
|
||||||
|
method = HttpMethod.Post
|
||||||
|
url("/notifications/register_device")
|
||||||
|
setBody(FcmTokenDTO(token))
|
||||||
|
contentType(ContentType.Application.Json)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.prodhack.moscow2025.domain.interfaces
|
||||||
|
|
||||||
|
interface FCMRepository {
|
||||||
|
|
||||||
|
suspend fun sendFCMToken(token: String)
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.prodhack.moscow2025.domain.usecase.auth
|
||||||
|
|
||||||
|
import com.prodhack.moscow2025.domain.interfaces.FCMRepository
|
||||||
|
import org.koin.core.annotation.Single
|
||||||
|
|
||||||
|
@Single
|
||||||
|
class SendFCMTokenUseCase(private val fcmRepository: FCMRepository) {
|
||||||
|
suspend operator fun invoke(token: String) = fcmRepository.sendFCMToken(token = token)
|
||||||
|
}
|
||||||
@@ -14,12 +14,15 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.google.firebase.messaging.FirebaseMessaging
|
import com.google.firebase.messaging.FirebaseMessaging
|
||||||
import com.prodhack.moscow2025.domain.usecase.auth.CheckSessionUseCase
|
import com.prodhack.moscow2025.domain.usecase.auth.CheckSessionUseCase
|
||||||
|
import com.prodhack.moscow2025.domain.usecase.auth.SendFCMTokenUseCase
|
||||||
import com.prodhack.moscow2025.domain.usecase.auth.SessionState
|
import com.prodhack.moscow2025.domain.usecase.auth.SessionState
|
||||||
import com.prodhack.moscow2025.presentation.navigation.AppDestination
|
import com.prodhack.moscow2025.presentation.navigation.AppDestination
|
||||||
import com.prodhack.moscow2025.presentation.navigation.TTasksApp
|
import com.prodhack.moscow2025.presentation.navigation.TTasksApp
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import kotlin.getValue
|
import kotlin.getValue
|
||||||
@@ -32,6 +35,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
private val checkSessionUseCase: CheckSessionUseCase by inject()
|
private val checkSessionUseCase: CheckSessionUseCase by inject()
|
||||||
|
|
||||||
|
private val sendFCMTokenUseCase: SendFCMTokenUseCase by inject()
|
||||||
private val sessionDestinationState = MutableStateFlow<AppDestination?>(null)
|
private val sessionDestinationState = MutableStateFlow<AppDestination?>(null)
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -65,20 +69,13 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
val sessionDestination by sessionDestinationState.collectAsState()
|
val sessionDestination by sessionDestinationState.collectAsState()
|
||||||
TTasksApp(sessionDestination = sessionDestination, context = this)
|
TTasksApp(
|
||||||
LaunchedEffect(Unit) {
|
sessionDestination = sessionDestination,
|
||||||
requestPermissions(
|
context = this,
|
||||||
arrayOf(Manifest.permission.ACCESS_NOTIFICATION_POLICY), 123
|
requestNotifyPermissions = {
|
||||||
)
|
|
||||||
FirebaseMessaging.getInstance().token
|
|
||||||
.addOnCompleteListener { task ->
|
|
||||||
if (task.isSuccessful) {
|
|
||||||
val token = task.result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkAndRequestNotificationPermission()
|
checkAndRequestNotificationPermission()
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,12 +86,10 @@ class MainActivity : ComponentActivity() {
|
|||||||
this,
|
this,
|
||||||
Manifest.permission.POST_NOTIFICATIONS
|
Manifest.permission.POST_NOTIFICATIONS
|
||||||
) == PackageManager.PERMISSION_GRANTED -> {
|
) == PackageManager.PERMISSION_GRANTED -> {
|
||||||
// Разрешение уже есть, получаем токен
|
|
||||||
getFCMToken()
|
getFCMToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
// Запрашиваем разрешение
|
|
||||||
requestPermissions(
|
requestPermissions(
|
||||||
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
|
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
|
||||||
123
|
123
|
||||||
@@ -102,17 +97,19 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Для версий ниже Android 13 разрешение не требуется
|
|
||||||
getFCMToken()
|
getFCMToken()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFCMToken() {
|
fun getFCMToken() {
|
||||||
FirebaseMessaging.getInstance().token
|
FirebaseMessaging.getInstance().token
|
||||||
.addOnCompleteListener { task ->
|
.addOnCompleteListener { task ->
|
||||||
if (task.isSuccessful) {
|
if (task.isSuccessful) {
|
||||||
val token = task.result
|
val token = task.result
|
||||||
Log.d("TOKEN", token)
|
Log.d("TOKEN", token)
|
||||||
|
lifecycleScope.launch {
|
||||||
|
sendFCMTokenUseCase(token)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.e("TOKEN", "Failed to get token", task.exception)
|
Log.e("TOKEN", "Failed to get token", task.exception)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import com.prodhack.moscow2025.presentation.utils.ui.SnackbarStyle
|
|||||||
fun TTasksApp(
|
fun TTasksApp(
|
||||||
appState: TTasksAppState = rememberTTasksAppState(),
|
appState: TTasksAppState = rememberTTasksAppState(),
|
||||||
context: Context,
|
context: Context,
|
||||||
|
requestNotifyPermissions: () -> Unit,
|
||||||
sessionDestination: AppDestination? = null
|
sessionDestination: AppDestination? = null
|
||||||
) {
|
) {
|
||||||
MoscowHackatonTemplateTheme {
|
MoscowHackatonTemplateTheme {
|
||||||
@@ -99,7 +100,8 @@ fun TTasksApp(
|
|||||||
modifier = Modifier.padding(padding),
|
modifier = Modifier.padding(padding),
|
||||||
sessionDestination = sessionDestination,
|
sessionDestination = sessionDestination,
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
context = context
|
context = context,
|
||||||
|
requestNotifyPermissions = requestNotifyPermissions
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ fun TTasksNavHost(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
sessionDestination: AppDestination? = null,
|
sessionDestination: AppDestination? = null,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
requestNotifyPermissions: () -> Unit,
|
||||||
snackbarHostState: SnackbarHostState
|
snackbarHostState: SnackbarHostState
|
||||||
) {
|
) {
|
||||||
val startDestination = sessionDestination?.route ?: AppDestination.Login.route
|
val startDestination = sessionDestination?.route ?: AppDestination.Login.route
|
||||||
@@ -100,7 +101,8 @@ fun TTasksNavHost(
|
|||||||
})
|
})
|
||||||
}, openCreateResume = {
|
}, openCreateResume = {
|
||||||
navController.navigate(AppDestination.ResumeCreation.route)
|
navController.navigate(AppDestination.ResumeCreation.route)
|
||||||
}
|
},
|
||||||
|
requestNotifyPermissions = requestNotifyPermissions
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.prodhack.moscow2025.presentation.screens.main
|
package com.prodhack.moscow2025.presentation.screens.main
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -21,6 +22,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
@@ -30,6 +32,7 @@ import androidx.compose.ui.unit.sp
|
|||||||
import androidx.paging.LoadState
|
import androidx.paging.LoadState
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
|
import com.google.firebase.messaging.FirebaseMessaging
|
||||||
import com.prodhack.moscow2025.R
|
import com.prodhack.moscow2025.R
|
||||||
import com.prodhack.moscow2025.presentation.components.standart.BigButton
|
import com.prodhack.moscow2025.presentation.components.standart.BigButton
|
||||||
import com.prodhack.moscow2025.presentation.components.standart.TopLogo
|
import com.prodhack.moscow2025.presentation.components.standart.TopLogo
|
||||||
@@ -45,8 +48,13 @@ fun ErrorCollectorScope.MainScreen(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
openResumeDetails: (String) -> Unit,
|
openResumeDetails: (String) -> Unit,
|
||||||
openCreateResume: () -> Unit,
|
openCreateResume: () -> Unit,
|
||||||
|
requestNotifyPermissions: () -> Unit,
|
||||||
viewModel: MainScreenViewModel = koinViewModel()
|
viewModel: MainScreenViewModel = koinViewModel()
|
||||||
) {
|
) {
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
requestNotifyPermissions()
|
||||||
|
}
|
||||||
|
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
val items = viewModel.resumeList.collectAsLazyPagingItems()
|
val items = viewModel.resumeList.collectAsLazyPagingItems()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user