fcm token sending

This commit is contained in:
MaximOksiuta
2025-11-23 15:22:48 +03:00
parent 28285be9da
commit 98a9216515
8 changed files with 85 additions and 22 deletions
@@ -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
)
@@ -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.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.lifecycle.lifecycleScope
import com.google.firebase.messaging.FirebaseMessaging
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.presentation.navigation.AppDestination
import com.prodhack.moscow2025.presentation.navigation.TTasksApp
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.koin.android.ext.android.inject
import kotlin.getValue
@@ -32,6 +35,7 @@ class MainActivity : ComponentActivity() {
private val checkSessionUseCase: CheckSessionUseCase by inject()
private val sendFCMTokenUseCase: SendFCMTokenUseCase by inject()
private val sessionDestinationState = MutableStateFlow<AppDestination?>(null)
override fun onCreate(savedInstanceState: Bundle?) {
@@ -65,20 +69,13 @@ class MainActivity : ComponentActivity() {
setContent {
val sessionDestination by sessionDestinationState.collectAsState()
TTasksApp(sessionDestination = sessionDestination, context = this)
LaunchedEffect(Unit) {
requestPermissions(
arrayOf(Manifest.permission.ACCESS_NOTIFICATION_POLICY), 123
)
FirebaseMessaging.getInstance().token
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val token = task.result
}
}
TTasksApp(
sessionDestination = sessionDestination,
context = this,
requestNotifyPermissions = {
checkAndRequestNotificationPermission()
}
)
}
}
@@ -89,12 +86,10 @@ class MainActivity : ComponentActivity() {
this,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED -> {
// Разрешение уже есть, получаем токен
getFCMToken()
}
else -> {
// Запрашиваем разрешение
requestPermissions(
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
123
@@ -102,17 +97,19 @@ class MainActivity : ComponentActivity() {
}
}
} else {
// Для версий ниже Android 13 разрешение не требуется
getFCMToken()
}
}
private fun getFCMToken() {
fun getFCMToken() {
FirebaseMessaging.getInstance().token
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val token = task.result
Log.d("TOKEN", token)
lifecycleScope.launch {
sendFCMTokenUseCase(token)
}
} else {
Log.e("TOKEN", "Failed to get token", task.exception)
}
@@ -26,6 +26,7 @@ import com.prodhack.moscow2025.presentation.utils.ui.SnackbarStyle
fun TTasksApp(
appState: TTasksAppState = rememberTTasksAppState(),
context: Context,
requestNotifyPermissions: () -> Unit,
sessionDestination: AppDestination? = null
) {
MoscowHackatonTemplateTheme {
@@ -99,7 +100,8 @@ fun TTasksApp(
modifier = Modifier.padding(padding),
sessionDestination = sessionDestination,
snackbarHostState = snackbarHostState,
context = context
context = context,
requestNotifyPermissions = requestNotifyPermissions
)
}
}
@@ -28,6 +28,7 @@ fun TTasksNavHost(
modifier: Modifier = Modifier,
sessionDestination: AppDestination? = null,
context: Context,
requestNotifyPermissions: () -> Unit,
snackbarHostState: SnackbarHostState
) {
val startDestination = sessionDestination?.route ?: AppDestination.Login.route
@@ -100,7 +101,8 @@ fun TTasksNavHost(
})
}, openCreateResume = {
navController.navigate(AppDestination.ResumeCreation.route)
}
},
requestNotifyPermissions = requestNotifyPermissions
)
}
@@ -1,5 +1,6 @@
package com.prodhack.moscow2025.presentation.screens.main
import android.Manifest
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -21,6 +22,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
@@ -30,6 +32,7 @@ import androidx.compose.ui.unit.sp
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.google.firebase.messaging.FirebaseMessaging
import com.prodhack.moscow2025.R
import com.prodhack.moscow2025.presentation.components.standart.BigButton
import com.prodhack.moscow2025.presentation.components.standart.TopLogo
@@ -45,8 +48,13 @@ fun ErrorCollectorScope.MainScreen(
modifier: Modifier = Modifier,
openResumeDetails: (String) -> Unit,
openCreateResume: () -> Unit,
requestNotifyPermissions: () -> Unit,
viewModel: MainScreenViewModel = koinViewModel()
) {
LaunchedEffect(Unit) {
requestNotifyPermissions()
}
Box(modifier = modifier) {
val items = viewModel.resumeList.collectAsLazyPagingItems()