feat: Update RosettaDatabase to include Message and Dialog entities, increment version to 2
feat: Implement message packets for sending and receiving messages, including delivery and read notifications feat: Enhance ProtocolManager to handle message sending, delivery, and typing status with appropriate logging feat: Refactor ChatDetailScreen to utilize ChatViewModel for managing chat state and message input feat: Create ChatViewModel to manage chat messages, input state, and packet listeners for incoming messages build: Add KSP plugin for annotation processing and configure Java 17 for the build environment
This commit is contained in:
@@ -186,3 +186,185 @@ class PacketOnlineSubscribe : Packet() {
|
||||
return stream
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// MESSAGE PACKETS - Как в React Native версии
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Типы вложений
|
||||
*/
|
||||
enum class AttachmentType(val value: Int) {
|
||||
IMAGE(0), // Изображение
|
||||
MESSAGES(1), // Reply (цитата сообщения)
|
||||
FILE(2); // Файл
|
||||
|
||||
companion object {
|
||||
fun fromInt(value: Int) = entries.firstOrNull { it.value == value } ?: IMAGE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Статус доставки сообщения
|
||||
*/
|
||||
enum class DeliveryStatus(val value: Int) {
|
||||
WAITING(0), // Ожидает отправки
|
||||
DELIVERED(1), // Доставлено
|
||||
ERROR(2); // Ошибка
|
||||
|
||||
companion object {
|
||||
fun fromInt(value: Int) = entries.firstOrNull { it.value == value } ?: WAITING
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Вложение к сообщению
|
||||
*/
|
||||
data class MessageAttachment(
|
||||
val id: String,
|
||||
val blob: String, // Base64 данные или пусто для CDN
|
||||
val type: AttachmentType,
|
||||
val preview: String = "", // Метаданные: "UUID::metadata" или "filesize::filename"
|
||||
val width: Int = 0,
|
||||
val height: Int = 0
|
||||
)
|
||||
|
||||
/**
|
||||
* Message packet (ID: 0x06)
|
||||
* Отправка и получение сообщений
|
||||
*/
|
||||
class PacketMessage : Packet() {
|
||||
var fromPublicKey: String = ""
|
||||
var toPublicKey: String = ""
|
||||
var content: String = "" // Зашифрованный текст
|
||||
var chachaKey: String = "" // RSA зашифрованный ключ
|
||||
var timestamp: Long = 0
|
||||
var privateKey: String = "" // Hash приватного ключа (для авторизации)
|
||||
var messageId: String = ""
|
||||
var attachments: List<MessageAttachment> = emptyList()
|
||||
|
||||
override fun getPacketId(): Int = 0x06
|
||||
|
||||
override fun receive(stream: Stream) {
|
||||
fromPublicKey = stream.readString()
|
||||
toPublicKey = stream.readString()
|
||||
content = stream.readString()
|
||||
chachaKey = stream.readString()
|
||||
timestamp = stream.readInt64()
|
||||
privateKey = stream.readString()
|
||||
messageId = stream.readString()
|
||||
|
||||
val attachmentCount = stream.readInt8()
|
||||
val attachmentsList = mutableListOf<MessageAttachment>()
|
||||
for (i in 0 until attachmentCount) {
|
||||
attachmentsList.add(MessageAttachment(
|
||||
id = stream.readString(),
|
||||
preview = stream.readString(),
|
||||
blob = stream.readString(),
|
||||
type = AttachmentType.fromInt(stream.readInt8())
|
||||
))
|
||||
}
|
||||
attachments = attachmentsList
|
||||
}
|
||||
|
||||
override fun send(): Stream {
|
||||
val stream = Stream()
|
||||
stream.writeInt16(getPacketId())
|
||||
stream.writeString(fromPublicKey)
|
||||
stream.writeString(toPublicKey)
|
||||
stream.writeString(content)
|
||||
stream.writeString(chachaKey)
|
||||
stream.writeInt64(timestamp)
|
||||
stream.writeString(privateKey)
|
||||
stream.writeString(messageId)
|
||||
stream.writeInt8(attachments.size)
|
||||
|
||||
for (attachment in attachments) {
|
||||
stream.writeString(attachment.id)
|
||||
stream.writeString(attachment.preview)
|
||||
stream.writeString(attachment.blob)
|
||||
stream.writeInt8(attachment.type.value)
|
||||
}
|
||||
|
||||
return stream
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read packet (ID: 0x07)
|
||||
* Уведомление о прочтении сообщения
|
||||
*/
|
||||
class PacketRead : Packet() {
|
||||
var messageId: String = ""
|
||||
var fromPublicKey: String = ""
|
||||
var toPublicKey: String = ""
|
||||
var privateKey: String = ""
|
||||
|
||||
override fun getPacketId(): Int = 0x07
|
||||
|
||||
override fun receive(stream: Stream) {
|
||||
messageId = stream.readString()
|
||||
fromPublicKey = stream.readString()
|
||||
toPublicKey = stream.readString()
|
||||
}
|
||||
|
||||
override fun send(): Stream {
|
||||
val stream = Stream()
|
||||
stream.writeInt16(getPacketId())
|
||||
stream.writeString(messageId)
|
||||
stream.writeString(fromPublicKey)
|
||||
stream.writeString(toPublicKey)
|
||||
stream.writeString(privateKey)
|
||||
return stream
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delivery packet (ID: 0x08)
|
||||
* Уведомление о доставке сообщения
|
||||
*/
|
||||
class PacketDelivery : Packet() {
|
||||
var messageId: String = ""
|
||||
var toPublicKey: String = ""
|
||||
|
||||
override fun getPacketId(): Int = 0x08
|
||||
|
||||
override fun receive(stream: Stream) {
|
||||
messageId = stream.readString()
|
||||
toPublicKey = stream.readString()
|
||||
}
|
||||
|
||||
override fun send(): Stream {
|
||||
val stream = Stream()
|
||||
stream.writeInt16(getPacketId())
|
||||
stream.writeString(messageId)
|
||||
stream.writeString(toPublicKey)
|
||||
return stream
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Typing packet (ID: 0x0B)
|
||||
* Уведомление "печатает..."
|
||||
*/
|
||||
class PacketTyping : Packet() {
|
||||
var fromPublicKey: String = ""
|
||||
var toPublicKey: String = ""
|
||||
var privateKey: String = ""
|
||||
|
||||
override fun getPacketId(): Int = 0x0B
|
||||
|
||||
override fun receive(stream: Stream) {
|
||||
fromPublicKey = stream.readString()
|
||||
toPublicKey = stream.readString()
|
||||
}
|
||||
|
||||
override fun send(): Stream {
|
||||
val stream = Stream()
|
||||
stream.writeInt16(getPacketId())
|
||||
stream.writeString(fromPublicKey)
|
||||
stream.writeString(toPublicKey)
|
||||
stream.writeString(privateKey)
|
||||
return stream
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.rosetta.messenger.network
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.rosetta.messenger.data.MessageRepository
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -18,11 +21,17 @@ object ProtocolManager {
|
||||
private const val SERVER_ADDRESS = "ws://46.28.71.12:3000"
|
||||
|
||||
private var protocol: Protocol? = null
|
||||
private var messageRepository: MessageRepository? = null
|
||||
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
// Debug logs for dev console
|
||||
private val _debugLogs = MutableStateFlow<List<String>>(emptyList())
|
||||
val debugLogs: StateFlow<List<String>> = _debugLogs.asStateFlow()
|
||||
|
||||
// Typing status
|
||||
private val _typingUsers = MutableStateFlow<Set<String>>(emptySet())
|
||||
val typingUsers: StateFlow<Set<String>> = _typingUsers.asStateFlow()
|
||||
|
||||
private val dateFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault())
|
||||
|
||||
fun addLog(message: String) {
|
||||
@@ -36,6 +45,62 @@ object ProtocolManager {
|
||||
_debugLogs.value = emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализация с контекстом для доступа к MessageRepository
|
||||
*/
|
||||
fun initialize(context: Context) {
|
||||
messageRepository = MessageRepository.getInstance(context)
|
||||
setupPacketHandlers()
|
||||
}
|
||||
|
||||
/**
|
||||
* Настройка обработчиков пакетов
|
||||
*/
|
||||
private fun setupPacketHandlers() {
|
||||
// Обработчик входящих сообщений (0x06)
|
||||
waitPacket(0x06) { packet ->
|
||||
val messagePacket = packet as PacketMessage
|
||||
addLog("📩 Incoming message from ${messagePacket.fromPublicKey.take(16)}...")
|
||||
|
||||
scope.launch {
|
||||
messageRepository?.handleIncomingMessage(messagePacket)
|
||||
}
|
||||
}
|
||||
|
||||
// Обработчик доставки (0x08)
|
||||
waitPacket(0x08) { packet ->
|
||||
val deliveryPacket = packet as PacketDelivery
|
||||
addLog("✓ Delivered: ${deliveryPacket.messageId.take(16)}...")
|
||||
|
||||
scope.launch {
|
||||
messageRepository?.handleDelivery(deliveryPacket)
|
||||
}
|
||||
}
|
||||
|
||||
// Обработчик прочтения (0x07)
|
||||
waitPacket(0x07) { packet ->
|
||||
val readPacket = packet as PacketRead
|
||||
addLog("✓✓ Read: ${readPacket.messageId.take(16)}...")
|
||||
|
||||
scope.launch {
|
||||
messageRepository?.handleRead(readPacket)
|
||||
}
|
||||
}
|
||||
|
||||
// Обработчик typing (0x0B)
|
||||
waitPacket(0x0B) { packet ->
|
||||
val typingPacket = packet as PacketTyping
|
||||
addLog("⌨️ Typing: ${typingPacket.fromPublicKey.take(16)}...")
|
||||
|
||||
// Добавляем в set и удаляем через 3 секунды
|
||||
_typingUsers.value = _typingUsers.value + typingPacket.fromPublicKey
|
||||
scope.launch {
|
||||
delay(3000)
|
||||
_typingUsers.value = _typingUsers.value - typingPacket.fromPublicKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create Protocol instance
|
||||
*/
|
||||
@@ -79,10 +144,17 @@ object ProtocolManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Send packet
|
||||
* Send packet (simplified)
|
||||
*/
|
||||
fun send(packet: Packet) {
|
||||
getProtocol().sendPacket(packet)
|
||||
}
|
||||
|
||||
/**
|
||||
* Send packet (legacy name)
|
||||
*/
|
||||
fun sendPacket(packet: Packet) {
|
||||
getProtocol().sendPacket(packet)
|
||||
send(packet)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,6 +185,7 @@ object ProtocolManager {
|
||||
fun destroy() {
|
||||
protocol?.destroy()
|
||||
protocol = null
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user