From 2de2f67e464f1873e7603a231e98d1ee9b52f83f Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Mon, 16 Feb 2026 11:22:55 +0500 Subject: [PATCH] Add network packet classes and enums for messaging functionality - Introduced `AttachmentType` enum to define various attachment types. - Added `DeliveryStatus` enum to represent message delivery statuses. - Created `MessageAttachment` data class to encapsulate message attachments. - Implemented `OnlineState` enum to manage user online statuses. - Developed various packet classes: `Packet`, `PacketHandshake`, `PacketResult`, `PacketSearch`, `PacketUserInfo`, `PacketOnlineState`, `PacketOnlineSubscribe`, `PacketMessage`, `PacketRead`, `PacketDelivery`, `PacketTyping`, `PacketChunk`, `PacketPushNotification`, `PacketRequestTransport`, and `PacketPushToken` for handling different messaging operations. - Removed the old `Packets.kt` file to streamline the codebase. - Added `SearchUser` data class for user search results. --- .../messenger/network/AttachmentType.kt | 15 + .../messenger/network/DeliveryStatus.kt | 15 + .../messenger/network/MessageAttachment.kt | 14 + .../rosetta/messenger/network/OnlineState.kt | 13 + .../com/rosetta/messenger/network/Packet.kt | 10 + .../rosetta/messenger/network/PacketChunk.kt | 34 ++ .../messenger/network/PacketDelivery.kt | 28 + .../messenger/network/PacketHandshake.kt | 31 + .../messenger/network/PacketMessage.kt | 62 ++ .../messenger/network/PacketOnlineState.kt | 41 ++ .../network/PacketOnlineSubscribe.kt | 37 ++ .../network/PacketPushNotification.kt | 37 ++ .../messenger/network/PacketPushToken.kt | 31 + .../rosetta/messenger/network/PacketRead.kt | 31 + .../network/PacketRequestTransport.kt | 22 + .../rosetta/messenger/network/PacketResult.kt | 24 + .../rosetta/messenger/network/PacketSearch.kt | 40 ++ .../rosetta/messenger/network/PacketTyping.kt | 28 + .../messenger/network/PacketUserInfo.kt | 29 + .../com/rosetta/messenger/network/Packets.kt | 540 ------------------ .../rosetta/messenger/network/SearchUser.kt | 9 + 21 files changed, 551 insertions(+), 540 deletions(-) create mode 100644 app/src/main/java/com/rosetta/messenger/network/AttachmentType.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/DeliveryStatus.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/MessageAttachment.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/OnlineState.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/Packet.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketChunk.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketDelivery.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketHandshake.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketMessage.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketOnlineState.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketOnlineSubscribe.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketPushNotification.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketPushToken.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketRead.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketRequestTransport.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketResult.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketSearch.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketTyping.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/PacketUserInfo.kt delete mode 100644 app/src/main/java/com/rosetta/messenger/network/Packets.kt create mode 100644 app/src/main/java/com/rosetta/messenger/network/SearchUser.kt diff --git a/app/src/main/java/com/rosetta/messenger/network/AttachmentType.kt b/app/src/main/java/com/rosetta/messenger/network/AttachmentType.kt new file mode 100644 index 0000000..064edc4 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/AttachmentType.kt @@ -0,0 +1,15 @@ +package com.rosetta.messenger.network + +/** + * Типы вложений + */ +enum class AttachmentType(val value: Int) { + IMAGE(0), // Изображение + MESSAGES(1), // Reply (цитата сообщения) + FILE(2), // Файл + AVATAR(3); // Аватар пользователя + + companion object { + fun fromInt(value: Int) = entries.firstOrNull { it.value == value } ?: IMAGE + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/DeliveryStatus.kt b/app/src/main/java/com/rosetta/messenger/network/DeliveryStatus.kt new file mode 100644 index 0000000..4b888f3 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/DeliveryStatus.kt @@ -0,0 +1,15 @@ +package com.rosetta.messenger.network + +/** + * Статус доставки сообщения + */ +enum class DeliveryStatus(val value: Int) { + WAITING(0), // Ожидает отправки + DELIVERED(1), // Доставлено + ERROR(2), // Ошибка + READ(3); // Прочитано + + companion object { + fun fromInt(value: Int) = entries.firstOrNull { it.value == value } ?: WAITING + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/MessageAttachment.kt b/app/src/main/java/com/rosetta/messenger/network/MessageAttachment.kt new file mode 100644 index 0000000..91849fb --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/MessageAttachment.kt @@ -0,0 +1,14 @@ +package com.rosetta.messenger.network + +/** + * Вложение к сообщению + */ +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, + val localUri: String = "" // 🚀 Локальный URI для мгновенного отображения (optimistic UI) +) diff --git a/app/src/main/java/com/rosetta/messenger/network/OnlineState.kt b/app/src/main/java/com/rosetta/messenger/network/OnlineState.kt new file mode 100644 index 0000000..6fe52dc --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/OnlineState.kt @@ -0,0 +1,13 @@ +package com.rosetta.messenger.network + +/** + * Online State enum + */ +enum class OnlineState(val value: Int) { + ONLINE(0), + OFFLINE(1); + + companion object { + fun fromBoolean(isOnline: Boolean) = if (isOnline) ONLINE else OFFLINE + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/Packet.kt b/app/src/main/java/com/rosetta/messenger/network/Packet.kt new file mode 100644 index 0000000..8844a9c --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/Packet.kt @@ -0,0 +1,10 @@ +package com.rosetta.messenger.network + +/** + * Base class for all protocol packets + */ +abstract class Packet { + abstract fun getPacketId(): Int + abstract fun receive(stream: Stream) + abstract fun send(): Stream +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketChunk.kt b/app/src/main/java/com/rosetta/messenger/network/PacketChunk.kt new file mode 100644 index 0000000..75eeb49 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketChunk.kt @@ -0,0 +1,34 @@ +package com.rosetta.messenger.network + +/** + * Chunk packet (ID: 0x09) + * Для разбиения больших пакетов на части (как в Desktop) + * ВАЖНО: chunkIndex и totalChunks - Int16, не Int32! + */ +class PacketChunk : Packet() { + var chunkId: String = "" + var chunkIndex: Int = 0 + var totalChunks: Int = 0 + var data: ByteArray = ByteArray(0) + + override fun getPacketId(): Int = 0x09 + + override fun receive(stream: Stream) { + // В Desktop: readInt16 для index и total + chunkIndex = stream.readInt16() + totalChunks = stream.readInt16() + chunkId = stream.readString() + data = stream.readBytes() + } + + override fun send(): Stream { + val stream = Stream() + stream.writeInt16(getPacketId()) + // В Desktop: writeInt16 для index и total + stream.writeInt16(chunkIndex) + stream.writeInt16(totalChunks) + stream.writeString(chunkId) + stream.writeBytes(data) + return stream + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketDelivery.kt b/app/src/main/java/com/rosetta/messenger/network/PacketDelivery.kt new file mode 100644 index 0000000..ec66567 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketDelivery.kt @@ -0,0 +1,28 @@ +package com.rosetta.messenger.network + +/** + * Delivery packet (ID: 0x08) + * Уведомление о доставке сообщения + * Порядок полей как в React Native: toPublicKey, messageId + */ +class PacketDelivery : Packet() { + var messageId: String = "" + var toPublicKey: String = "" + + override fun getPacketId(): Int = 0x08 + + override fun receive(stream: Stream) { + // React Native читает: toPublicKey, messageId + toPublicKey = stream.readString() + messageId = stream.readString() + } + + override fun send(): Stream { + val stream = Stream() + stream.writeInt16(getPacketId()) + // React Native пишет: toPublicKey, messageId + stream.writeString(toPublicKey) + stream.writeString(messageId) + return stream + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketHandshake.kt b/app/src/main/java/com/rosetta/messenger/network/PacketHandshake.kt new file mode 100644 index 0000000..56da610 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketHandshake.kt @@ -0,0 +1,31 @@ +package com.rosetta.messenger.network + +/** + * Handshake packet (ID: 0x00) + * First packet sent by client to authenticate with the server + */ +class PacketHandshake : Packet() { + var privateKey: String = "" + var publicKey: String = "" + var protocolVersion: Int = 1 + var heartbeatInterval: Int = 15 + + override fun getPacketId(): Int = 0x00 + + override fun receive(stream: Stream) { + privateKey = stream.readString() + publicKey = stream.readString() + protocolVersion = stream.readInt8() + heartbeatInterval = stream.readInt8() + } + + override fun send(): Stream { + val stream = Stream() + stream.writeInt16(getPacketId()) + stream.writeString(privateKey) + stream.writeString(publicKey) + stream.writeInt8(protocolVersion) + stream.writeInt8(heartbeatInterval) + return stream + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketMessage.kt b/app/src/main/java/com/rosetta/messenger/network/PacketMessage.kt new file mode 100644 index 0000000..7f6d6a5 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketMessage.kt @@ -0,0 +1,62 @@ +package com.rosetta.messenger.network + +/** + * 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 = 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() + 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 + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketOnlineState.kt b/app/src/main/java/com/rosetta/messenger/network/PacketOnlineState.kt new file mode 100644 index 0000000..78fffa7 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketOnlineState.kt @@ -0,0 +1,41 @@ +package com.rosetta.messenger.network + +/** + * Public key with online state + */ +data class PublicKeyOnlineState( + val publicKey: String, + val state: OnlineState +) + +/** + * Online State packet (ID: 0x05) + * Notify about user online status + * Формат как в React Native: массив {publicKey, state} + */ +class PacketOnlineState : Packet() { + var publicKeysState: MutableList = mutableListOf() + + override fun getPacketId(): Int = 0x05 + + override fun receive(stream: Stream) { + val count = stream.readInt8() + publicKeysState.clear() + repeat(count) { + val publicKey = stream.readString() + val isOnline = stream.readBoolean() + publicKeysState.add(PublicKeyOnlineState(publicKey, OnlineState.fromBoolean(isOnline))) + } + } + + override fun send(): Stream { + val stream = Stream() + stream.writeInt16(getPacketId()) + stream.writeInt8(publicKeysState.size) + publicKeysState.forEach { item -> + stream.writeString(item.publicKey) + stream.writeBoolean(item.state == OnlineState.ONLINE) + } + return stream + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketOnlineSubscribe.kt b/app/src/main/java/com/rosetta/messenger/network/PacketOnlineSubscribe.kt new file mode 100644 index 0000000..95c0e1b --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketOnlineSubscribe.kt @@ -0,0 +1,37 @@ +package com.rosetta.messenger.network + +/** + * Online Subscribe packet (ID: 0x04) + * Subscribe to user online status updates + * Формат как в React Native: privateKey + array of publicKeys + */ +class PacketOnlineSubscribe : Packet() { + var privateKey: String = "" + var publicKeys: MutableList = mutableListOf() + + override fun getPacketId(): Int = 0x04 + + override fun receive(stream: Stream) { + privateKey = stream.readString() + val keysCount = stream.readInt16() + publicKeys.clear() + repeat(keysCount) { + publicKeys.add(stream.readString()) + } + } + + override fun send(): Stream { + val stream = Stream() + stream.writeInt16(getPacketId()) + stream.writeString(privateKey) + stream.writeInt16(publicKeys.size) + publicKeys.forEach { key -> + stream.writeString(key) + } + return stream + } + + fun addPublicKey(publicKey: String) { + publicKeys.add(publicKey) + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketPushNotification.kt b/app/src/main/java/com/rosetta/messenger/network/PacketPushNotification.kt new file mode 100644 index 0000000..6fc13e5 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketPushNotification.kt @@ -0,0 +1,37 @@ +package com.rosetta.messenger.network + +/** + * Push Notification Action + */ +enum class PushNotificationAction(val value: Int) { + SUBSCRIBE(0), + UNSUBSCRIBE(1) +} + +/** + * Push Notification packet (ID: 0x10) + * Отправка FCM/APNS токена на сервер для push-уведомлений (новый формат) + * Совместим с React Native версией + */ +class PacketPushNotification : Packet() { + var notificationsToken: String = "" + var action: PushNotificationAction = PushNotificationAction.SUBSCRIBE + + override fun getPacketId(): Int = 0x10 + + override fun receive(stream: Stream) { + notificationsToken = stream.readString() + action = when (stream.readInt8()) { + 1 -> PushNotificationAction.UNSUBSCRIBE + else -> PushNotificationAction.SUBSCRIBE + } + } + + override fun send(): Stream { + val stream = Stream() + stream.writeInt16(getPacketId()) + stream.writeString(notificationsToken) + stream.writeInt8(action.value) + return stream + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketPushToken.kt b/app/src/main/java/com/rosetta/messenger/network/PacketPushToken.kt new file mode 100644 index 0000000..45e0243 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketPushToken.kt @@ -0,0 +1,31 @@ +package com.rosetta.messenger.network + +/** + * Push Token packet (ID: 0x0A) - DEPRECATED + * Старый формат, заменен на PacketPushNotification (0x10) + */ +class PacketPushToken : Packet() { + var privateKey: String = "" + var publicKey: String = "" + var pushToken: String = "" + var platform: String = "android" // "android" или "ios" + + override fun getPacketId(): Int = 0x0A + + override fun receive(stream: Stream) { + privateKey = stream.readString() + publicKey = stream.readString() + pushToken = stream.readString() + platform = stream.readString() + } + + override fun send(): Stream { + val stream = Stream() + stream.writeInt16(getPacketId()) + stream.writeString(privateKey) + stream.writeString(publicKey) + stream.writeString(pushToken) + stream.writeString(platform) + return stream + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketRead.kt b/app/src/main/java/com/rosetta/messenger/network/PacketRead.kt new file mode 100644 index 0000000..eccd150 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketRead.kt @@ -0,0 +1,31 @@ +package com.rosetta.messenger.network + +/** + * Read packet (ID: 0x07) + * Уведомление о прочтении сообщения + * Порядок полей как в Desktop: privateKey, fromPublicKey, toPublicKey + */ +class PacketRead : Packet() { + var privateKey: String = "" + var fromPublicKey: String = "" + var toPublicKey: String = "" + + override fun getPacketId(): Int = 0x07 + + override fun receive(stream: Stream) { + // Desktop: privateKey, fromPublicKey, toPublicKey + privateKey = stream.readString() + fromPublicKey = stream.readString() + toPublicKey = stream.readString() + } + + override fun send(): Stream { + val stream = Stream() + stream.writeInt16(getPacketId()) + // Desktop: privateKey, fromPublicKey, toPublicKey + stream.writeString(privateKey) + stream.writeString(fromPublicKey) + stream.writeString(toPublicKey) + return stream + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketRequestTransport.kt b/app/src/main/java/com/rosetta/messenger/network/PacketRequestTransport.kt new file mode 100644 index 0000000..195873d --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketRequestTransport.kt @@ -0,0 +1,22 @@ +package com.rosetta.messenger.network + +/** + * Request Transport packet (ID: 0x0F) + * Запрос адреса транспортного сервера для загрузки/скачивания файлов + */ +class PacketRequestTransport : Packet() { + var transportServer: String = "" + + override fun getPacketId(): Int = 0x0F + + override fun receive(stream: Stream) { + transportServer = stream.readString() + } + + override fun send(): Stream { + val stream = Stream() + stream.writeInt16(getPacketId()) + stream.writeString(transportServer) + return stream + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketResult.kt b/app/src/main/java/com/rosetta/messenger/network/PacketResult.kt new file mode 100644 index 0000000..9131fab --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketResult.kt @@ -0,0 +1,24 @@ +package com.rosetta.messenger.network + +/** + * Result packet (ID: 0x02) + * Server response for various operations + * Desktop uses: readInt16() for resultCode only + */ +class PacketResult : Packet() { + var resultCode: Int = 0 + + override fun getPacketId(): Int = 0x02 + + override fun receive(stream: Stream) { + // Desktop: this.resultCode = stream.readInt16(); + resultCode = stream.readInt16() + } + + override fun send(): Stream { + val stream = Stream() + stream.writeInt16(getPacketId()) + stream.writeInt16(resultCode) + return stream + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketSearch.kt b/app/src/main/java/com/rosetta/messenger/network/PacketSearch.kt new file mode 100644 index 0000000..bb5933e --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketSearch.kt @@ -0,0 +1,40 @@ +package com.rosetta.messenger.network + +/** + * Search packet (ID: 0x03) + * Search for users by username or public key + */ +class PacketSearch : Packet() { + var privateKey: String = "" + var search: String = "" + var users: List = emptyList() + + override fun getPacketId(): Int = 0x03 + + override fun receive(stream: Stream) { + privateKey = stream.readString() + search = stream.readString() + val userCount = stream.readInt16() // Int16, not Int32! + val usersList = mutableListOf() + for (i in 0 until userCount) { + // Order: username, title, publicKey, verified, online (matching React Native) + val user = SearchUser( + username = stream.readString(), + title = stream.readString(), + publicKey = stream.readString(), + verified = stream.readInt8(), + online = stream.readInt8() + ) + usersList.add(user) + } + users = usersList + } + + override fun send(): Stream { + val stream = Stream() + stream.writeInt16(getPacketId()) + stream.writeString(privateKey) + stream.writeString(search) + return stream + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketTyping.kt b/app/src/main/java/com/rosetta/messenger/network/PacketTyping.kt new file mode 100644 index 0000000..c91cbf6 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketTyping.kt @@ -0,0 +1,28 @@ +package com.rosetta.messenger.network + +/** + * Typing packet (ID: 0x0B) + * Порядок полей как в React Native: privateKey, fromPublicKey, toPublicKey + */ +class PacketTyping : Packet() { + var privateKey: String = "" + var fromPublicKey: String = "" + var toPublicKey: String = "" + + override fun getPacketId(): Int = 0x0B + + override fun receive(stream: Stream) { + privateKey = stream.readString() + fromPublicKey = stream.readString() + toPublicKey = stream.readString() + } + + override fun send(): Stream { + val stream = Stream() + stream.writeInt16(getPacketId()) + stream.writeString(privateKey) + stream.writeString(fromPublicKey) + stream.writeString(toPublicKey) + return stream + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/PacketUserInfo.kt b/app/src/main/java/com/rosetta/messenger/network/PacketUserInfo.kt new file mode 100644 index 0000000..73a7ef1 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/PacketUserInfo.kt @@ -0,0 +1,29 @@ +package com.rosetta.messenger.network + +/** + * User Info packet (ID: 0x01) + * Get/Set user information + * Protocol order: username, title, privateKey (matching TypeScript version) + */ +class PacketUserInfo : Packet() { + var privateKey: String = "" + var username: String = "" + var title: String = "" + + override fun getPacketId(): Int = 0x01 + + override fun receive(stream: Stream) { + username = stream.readString() + title = stream.readString() + privateKey = stream.readString() + } + + override fun send(): Stream { + val stream = Stream() + stream.writeInt16(getPacketId()) + stream.writeString(username) + stream.writeString(title) + stream.writeString(privateKey) + return stream + } +} diff --git a/app/src/main/java/com/rosetta/messenger/network/Packets.kt b/app/src/main/java/com/rosetta/messenger/network/Packets.kt deleted file mode 100644 index 1fc445a..0000000 --- a/app/src/main/java/com/rosetta/messenger/network/Packets.kt +++ /dev/null @@ -1,540 +0,0 @@ -package com.rosetta.messenger.network - -/** - * Base class for all protocol packets - */ -abstract class Packet { - abstract fun getPacketId(): Int - abstract fun receive(stream: Stream) - abstract fun send(): Stream -} - -/** - * Handshake packet (ID: 0x00) - * First packet sent by client to authenticate with the server - */ -class PacketHandshake : Packet() { - var privateKey: String = "" - var publicKey: String = "" - var protocolVersion: Int = 1 - var heartbeatInterval: Int = 15 - - override fun getPacketId(): Int = 0x00 - - override fun receive(stream: Stream) { - privateKey = stream.readString() - publicKey = stream.readString() - protocolVersion = stream.readInt8() - heartbeatInterval = stream.readInt8() - } - - override fun send(): Stream { - val stream = Stream() - stream.writeInt16(getPacketId()) - stream.writeString(privateKey) - stream.writeString(publicKey) - stream.writeInt8(protocolVersion) - stream.writeInt8(heartbeatInterval) - return stream - } -} - -/** - * Result packet (ID: 0x02) - * Server response for various operations - * Desktop uses: readInt16() for resultCode only - */ -class PacketResult : Packet() { - var resultCode: Int = 0 - - override fun getPacketId(): Int = 0x02 - - override fun receive(stream: Stream) { - // Desktop: this.resultCode = stream.readInt16(); - resultCode = stream.readInt16() - } - - override fun send(): Stream { - val stream = Stream() - stream.writeInt16(getPacketId()) - stream.writeInt16(resultCode) - return stream - } -} - -/** - * Search packet (ID: 0x03) - * Search for users by username or public key - */ -class PacketSearch : Packet() { - var privateKey: String = "" - var search: String = "" - var users: List = emptyList() - - override fun getPacketId(): Int = 0x03 - - override fun receive(stream: Stream) { - privateKey = stream.readString() - search = stream.readString() - val userCount = stream.readInt16() // Int16, not Int32! - val usersList = mutableListOf() - for (i in 0 until userCount) { - // Order: username, title, publicKey, verified, online (matching React Native) - val user = SearchUser( - username = stream.readString(), - title = stream.readString(), - publicKey = stream.readString(), - verified = stream.readInt8(), - online = stream.readInt8() - ) - usersList.add(user) - } - users = usersList - } - - override fun send(): Stream { - val stream = Stream() - stream.writeInt16(getPacketId()) - stream.writeString(privateKey) - stream.writeString(search) - return stream - } -} - -data class SearchUser( - val publicKey: String, - val title: String, - val username: String, - val verified: Int, - val online: Int -) - -/** - * User Info packet (ID: 0x01) - * Get/Set user information - * Protocol order: username, title, privateKey (matching TypeScript version) - */ -class PacketUserInfo : Packet() { - var privateKey: String = "" - var username: String = "" - var title: String = "" - - override fun getPacketId(): Int = 0x01 - - override fun receive(stream: Stream) { - username = stream.readString() - title = stream.readString() - privateKey = stream.readString() - } - - override fun send(): Stream { - val stream = Stream() - stream.writeInt16(getPacketId()) - stream.writeString(username) - stream.writeString(title) - stream.writeString(privateKey) - return stream - } -} - -/** - * Online State enum - */ -enum class OnlineState(val value: Int) { - ONLINE(0), - OFFLINE(1); - - companion object { - fun fromBoolean(isOnline: Boolean) = if (isOnline) ONLINE else OFFLINE - } -} - -/** - * Public key with online state - */ -data class PublicKeyOnlineState( - val publicKey: String, - val state: OnlineState -) - -/** - * Online State packet (ID: 0x05) - * Notify about user online status - * Формат как в React Native: массив {publicKey, state} - */ -class PacketOnlineState : Packet() { - var publicKeysState: MutableList = mutableListOf() - - override fun getPacketId(): Int = 0x05 - - override fun receive(stream: Stream) { - val count = stream.readInt8() - publicKeysState.clear() - repeat(count) { - val publicKey = stream.readString() - val isOnline = stream.readBoolean() - publicKeysState.add(PublicKeyOnlineState(publicKey, OnlineState.fromBoolean(isOnline))) - } - } - - override fun send(): Stream { - val stream = Stream() - stream.writeInt16(getPacketId()) - stream.writeInt8(publicKeysState.size) - publicKeysState.forEach { item -> - stream.writeString(item.publicKey) - stream.writeBoolean(item.state == OnlineState.ONLINE) - } - return stream - } -} - -/** - * Online Subscribe packet (ID: 0x04) - * Subscribe to user online status updates - * Формат как в React Native: privateKey + array of publicKeys - */ -class PacketOnlineSubscribe : Packet() { - var privateKey: String = "" - var publicKeys: MutableList = mutableListOf() - - override fun getPacketId(): Int = 0x04 - - override fun receive(stream: Stream) { - privateKey = stream.readString() - val keysCount = stream.readInt16() - publicKeys.clear() - repeat(keysCount) { - publicKeys.add(stream.readString()) - } - } - - override fun send(): Stream { - val stream = Stream() - stream.writeInt16(getPacketId()) - stream.writeString(privateKey) - stream.writeInt16(publicKeys.size) - publicKeys.forEach { key -> - stream.writeString(key) - } - return stream - } - - fun addPublicKey(publicKey: String) { - publicKeys.add(publicKey) - } -} - -// ============================================================================ -// MESSAGE PACKETS - Как в React Native версии -// ============================================================================ - -/** - * Типы вложений - */ -enum class AttachmentType(val value: Int) { - IMAGE(0), // Изображение - MESSAGES(1), // Reply (цитата сообщения) - FILE(2), // Файл - AVATAR(3); // Аватар пользователя - - companion object { - fun fromInt(value: Int) = entries.firstOrNull { it.value == value } ?: IMAGE - } -} - -/** - * Статус доставки сообщения - */ -enum class DeliveryStatus(val value: Int) { - WAITING(0), // Ожидает отправки - DELIVERED(1), // Доставлено - ERROR(2), // Ошибка - READ(3); // Прочитано - - 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, - val localUri: String = "" // 🚀 Локальный URI для мгновенного отображения (optimistic UI) -) - -/** - * 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 = 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() - 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) - * Уведомление о прочтении сообщения - * Порядок полей как в Desktop: privateKey, fromPublicKey, toPublicKey - */ -class PacketRead : Packet() { - var privateKey: String = "" - var fromPublicKey: String = "" - var toPublicKey: String = "" - - override fun getPacketId(): Int = 0x07 - - override fun receive(stream: Stream) { - // Desktop: privateKey, fromPublicKey, toPublicKey - privateKey = stream.readString() - fromPublicKey = stream.readString() - toPublicKey = stream.readString() - } - - override fun send(): Stream { - val stream = Stream() - stream.writeInt16(getPacketId()) - // Desktop: privateKey, fromPublicKey, toPublicKey - stream.writeString(privateKey) - stream.writeString(fromPublicKey) - stream.writeString(toPublicKey) - return stream - } -} - -/** - * Delivery packet (ID: 0x08) - * Уведомление о доставке сообщения - * Порядок полей как в React Native: toPublicKey, messageId - */ -class PacketDelivery : Packet() { - var messageId: String = "" - var toPublicKey: String = "" - - override fun getPacketId(): Int = 0x08 - - override fun receive(stream: Stream) { - // React Native читает: toPublicKey, messageId - toPublicKey = stream.readString() - messageId = stream.readString() - } - - override fun send(): Stream { - val stream = Stream() - stream.writeInt16(getPacketId()) - // React Native пишет: toPublicKey, messageId - stream.writeString(toPublicKey) - stream.writeString(messageId) - return stream - } -} - -/** - * Typing packet (ID: 0x0B) - * Уведомление "печатает..." - */ -/** - * Typing packet (ID: 0x0B) - * Порядок полей как в React Native: privateKey, fromPublicKey, toPublicKey - */ -class PacketTyping : Packet() { - var privateKey: String = "" - var fromPublicKey: String = "" - var toPublicKey: String = "" - - override fun getPacketId(): Int = 0x0B - - override fun receive(stream: Stream) { - privateKey = stream.readString() - fromPublicKey = stream.readString() - toPublicKey = stream.readString() - } - - override fun send(): Stream { - val stream = Stream() - stream.writeInt16(getPacketId()) - stream.writeString(privateKey) - stream.writeString(fromPublicKey) - stream.writeString(toPublicKey) - return stream - } -} - -/** - * Chunk packet (ID: 0x09) - * Для разбиения больших пакетов на части (как в Desktop) - * ВАЖНО: chunkIndex и totalChunks - Int16, не Int32! - */ -class PacketChunk : Packet() { - var chunkId: String = "" - var chunkIndex: Int = 0 - var totalChunks: Int = 0 - var data: ByteArray = ByteArray(0) - - override fun getPacketId(): Int = 0x09 - - override fun receive(stream: Stream) { - // В Desktop: readInt16 для index и total - chunkIndex = stream.readInt16() - totalChunks = stream.readInt16() - chunkId = stream.readString() - data = stream.readBytes() - } - - override fun send(): Stream { - val stream = Stream() - stream.writeInt16(getPacketId()) - // В Desktop: writeInt16 для index и total - stream.writeInt16(chunkIndex) - stream.writeInt16(totalChunks) - stream.writeString(chunkId) - stream.writeBytes(data) - return stream - } -} - -/** - * Push Token packet (ID: 0x0A) - DEPRECATED - * Старый формат, заменен на PacketPushNotification (0x10) - */ -class PacketPushToken : Packet() { - var privateKey: String = "" - var publicKey: String = "" - var pushToken: String = "" - var platform: String = "android" // "android" или "ios" - - override fun getPacketId(): Int = 0x0A - - override fun receive(stream: Stream) { - privateKey = stream.readString() - publicKey = stream.readString() - pushToken = stream.readString() - platform = stream.readString() - } - - override fun send(): Stream { - val stream = Stream() - stream.writeInt16(getPacketId()) - stream.writeString(privateKey) - stream.writeString(publicKey) - stream.writeString(pushToken) - stream.writeString(platform) - return stream - } -} - -/** - * Push Notification Action - */ -enum class PushNotificationAction(val value: Int) { - SUBSCRIBE(0), - UNSUBSCRIBE(1) -} - -/** - * Push Notification packet (ID: 0x10) - * Отправка FCM/APNS токена на сервер для push-уведомлений (новый формат) - * Совместим с React Native версией - */ -class PacketPushNotification : Packet() { - var notificationsToken: String = "" - var action: PushNotificationAction = PushNotificationAction.SUBSCRIBE - - override fun getPacketId(): Int = 0x10 - - override fun receive(stream: Stream) { - notificationsToken = stream.readString() - action = when (stream.readInt8()) { - 1 -> PushNotificationAction.UNSUBSCRIBE - else -> PushNotificationAction.SUBSCRIBE - } - } - - override fun send(): Stream { - val stream = Stream() - stream.writeInt16(getPacketId()) - stream.writeString(notificationsToken) - stream.writeInt8(action.value) - return stream - } -} - -/** - * Request Transport packet (ID: 0x0F) - * Запрос адреса транспортного сервера для загрузки/скачивания файлов - */ -class PacketRequestTransport : Packet() { - var transportServer: String = "" - - override fun getPacketId(): Int = 0x0F - - override fun receive(stream: Stream) { - transportServer = stream.readString() - } - - override fun send(): Stream { - val stream = Stream() - stream.writeInt16(getPacketId()) - stream.writeString(transportServer) - return stream - } -} diff --git a/app/src/main/java/com/rosetta/messenger/network/SearchUser.kt b/app/src/main/java/com/rosetta/messenger/network/SearchUser.kt new file mode 100644 index 0000000..ac0fcb2 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/network/SearchUser.kt @@ -0,0 +1,9 @@ +package com.rosetta.messenger.network + +data class SearchUser( + val publicKey: String, + val title: String, + val username: String, + val verified: Int, + val online: Int +)