feat: Integrate Firebase Cloud Messaging for push notifications; add service to handle token and message reception
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
package com.rosetta.messenger.push
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.google.firebase.messaging.FirebaseMessagingService
|
||||
import com.google.firebase.messaging.RemoteMessage
|
||||
import com.rosetta.messenger.MainActivity
|
||||
import com.rosetta.messenger.R
|
||||
import com.rosetta.messenger.crypto.CryptoManager
|
||||
import com.rosetta.messenger.data.AccountManager
|
||||
import com.rosetta.messenger.network.ProtocolManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Firebase Cloud Messaging Service для обработки push-уведомлений
|
||||
*
|
||||
* Обрабатывает:
|
||||
* - Получение нового FCM токена
|
||||
* - Получение push-уведомлений о новых сообщениях
|
||||
* - Отображение уведомлений
|
||||
*/
|
||||
class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
|
||||
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
companion object {
|
||||
private const val TAG = "RosettaFCM"
|
||||
private const val CHANNEL_ID = "rosetta_messages"
|
||||
private const val CHANNEL_NAME = "Messages"
|
||||
private const val NOTIFICATION_ID = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Вызывается когда получен новый FCM токен
|
||||
* Отправляем его на сервер через протокол
|
||||
*/
|
||||
override fun onNewToken(token: String) {
|
||||
super.onNewToken(token)
|
||||
Log.d(TAG, "🔔 New FCM token (short): ${token.take(20)}...")
|
||||
Log.d(TAG, "🔔 New FCM token (FULL): $token")
|
||||
|
||||
// Сохраняем токен локально
|
||||
saveFcmToken(token)
|
||||
|
||||
// TODO: Отправляем токен на сервер если аккаунт уже залогинен
|
||||
/*
|
||||
serviceScope.launch {
|
||||
try {
|
||||
val accountManager = AccountManager(applicationContext)
|
||||
val currentAccount = accountManager.getCurrentAccount()
|
||||
|
||||
if (currentAccount != null) {
|
||||
Log.d(TAG, "📤 Sending FCM token to server for account: ${currentAccount.publicKey.take(10)}...")
|
||||
|
||||
// Отправляем через протокол
|
||||
val packet = PacketPushToken().apply {
|
||||
this.privateKey = CryptoManager.generatePrivateKeyHash(currentAccount.privateKey)
|
||||
this.publicKey = currentAccount.publicKey
|
||||
this.pushToken = token
|
||||
this.platform = "android"
|
||||
}
|
||||
|
||||
ProtocolManager.send(packet)
|
||||
Log.d(TAG, "✅ FCM token sent to server")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ Error sending FCM token to server", e)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Вызывается когда получено push-уведомление
|
||||
*/
|
||||
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
||||
super.onMessageReceived(remoteMessage)
|
||||
Log.d(TAG, "📬 Push notification received from: ${remoteMessage.from}")
|
||||
|
||||
// Обрабатываем data payload
|
||||
remoteMessage.data.isNotEmpty().let {
|
||||
Log.d(TAG, "📦 Message data payload: ${remoteMessage.data}")
|
||||
|
||||
val type = remoteMessage.data["type"]
|
||||
val senderPublicKey = remoteMessage.data["sender_public_key"]
|
||||
val senderName = remoteMessage.data["sender_name"] ?: senderPublicKey?.take(10) ?: "Unknown"
|
||||
val messagePreview = remoteMessage.data["message_preview"] ?: "New message"
|
||||
|
||||
when (type) {
|
||||
"new_message" -> {
|
||||
// Показываем уведомление о новом сообщении
|
||||
showMessageNotification(senderPublicKey, senderName, messagePreview)
|
||||
}
|
||||
"message_read" -> {
|
||||
// Сообщение прочитано - можно обновить UI если приложение открыто
|
||||
Log.d(TAG, "📖 Message read by $senderPublicKey")
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "⚠️ Unknown notification type: $type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Обрабатываем notification payload (если есть)
|
||||
remoteMessage.notification?.let {
|
||||
Log.d(TAG, "📨 Message Notification Body: ${it.body}")
|
||||
showSimpleNotification(it.title ?: "Rosetta", it.body ?: "New message")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Показать уведомление о новом сообщении
|
||||
*/
|
||||
private fun showMessageNotification(senderPublicKey: String?, senderName: String, messagePreview: String) {
|
||||
createNotificationChannel()
|
||||
|
||||
// Intent для открытия чата
|
||||
val intent = Intent(this, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
putExtra("open_chat", senderPublicKey)
|
||||
}
|
||||
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_launcher_foreground)
|
||||
.setContentTitle(senderName)
|
||||
.setContentText(messagePreview)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(pendingIntent)
|
||||
.build()
|
||||
|
||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.notify(NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
/**
|
||||
* Показать простое уведомление
|
||||
*/
|
||||
private fun showSimpleNotification(title: String, body: String) {
|
||||
createNotificationChannel()
|
||||
|
||||
val intent = Intent(this, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_launcher_foreground)
|
||||
.setContentTitle(title)
|
||||
.setContentText(body)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(pendingIntent)
|
||||
.build()
|
||||
|
||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.notify(NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать notification channel для Android 8+
|
||||
*/
|
||||
private fun createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
CHANNEL_NAME,
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
).apply {
|
||||
description = "Notifications for new messages"
|
||||
enableVibration(true)
|
||||
}
|
||||
|
||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить FCM токен в SharedPreferences
|
||||
*/
|
||||
private fun saveFcmToken(token: String) {
|
||||
val prefs = getSharedPreferences("rosetta_prefs", Context.MODE_PRIVATE)
|
||||
prefs.edit().putString("fcm_token", token).apply()
|
||||
Log.d(TAG, "💾 FCM token saved locally")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user