feat: Add logging for packet details before sending, including attachment count and details

This commit is contained in:
k1ngsterr1
2026-01-13 06:02:03 +05:00
parent c52b6c1799
commit bdede2784c
3 changed files with 53 additions and 13 deletions

View File

@@ -752,7 +752,11 @@ object MessageCrypto {
* Использует PBKDF2 + AES-CBC с тем же ключом что и основное сообщение
*
* В RN: encodeWithPassword(key.toString('utf-8'), JSON.stringify(reply))
* где key = Buffer.concat([chacha_key, nonce]) - 56 bytes as UTF-8 string
* где key = Buffer.concat([chacha_key, nonce]) - 56 bytes
*
* ВАЖНО: JavaScript Buffer.toString('utf-8') на невалидных UTF-8 байтах
* заменяет их на U+FFFD (replacement character). Это поведение нужно
* воспроизвести в Kotlin для совместимости.
*
* Формат выхода: "ivBase64:ciphertextBase64" (совместим с desktop)
*/
@@ -762,9 +766,11 @@ object MessageCrypto {
android.util.Log.d("MessageCrypto", " - ReplyJson length: ${replyJson.length}")
android.util.Log.d("MessageCrypto", " - PlainKeyAndNonce length: ${plainKeyAndNonce.size}")
// Convert keyAndNonce to UTF-8 string (as password) - same as RN: key.toString('utf-8')
val password = String(plainKeyAndNonce, Charsets.UTF_8)
// Convert keyAndNonce to string - simulate JS Buffer.toString('utf-8') behavior
// which replaces invalid UTF-8 sequences with U+FFFD
val password = bytesToJsUtf8String(plainKeyAndNonce)
android.util.Log.d("MessageCrypto", " - Password length: ${password.length}")
android.util.Log.d("MessageCrypto", " - Password char codes: ${password.take(5).map { it.code }}")
// Compress with pako (deflate)
val deflater = java.util.zip.Deflater()
@@ -786,7 +792,7 @@ object MessageCrypto {
)
val secretKey = factory.generateSecret(spec)
val keyBytes = secretKey.encoded
android.util.Log.d("MessageCrypto", " - PBKDF2 key derived: ${keyBytes.size} bytes")
android.util.Log.d("MessageCrypto", " - PBKDF2 key derived: ${keyBytes.toHex()}")
// Generate random IV (16 bytes)
val iv = ByteArray(16)
@@ -814,6 +820,25 @@ object MessageCrypto {
replyJson
}
}
/**
* Converts bytes to string mimicking JavaScript's Buffer.toString('utf-8') behavior.
* Invalid UTF-8 sequences are replaced with U+FFFD (replacement character).
*/
private fun bytesToJsUtf8String(bytes: ByteArray): String {
// CharsetDecoder with REPLACE action mimics JS behavior
val decoder = Charsets.UTF_8.newDecoder()
decoder.onMalformedInput(java.nio.charset.CodingErrorAction.REPLACE)
decoder.onUnmappableCharacter(java.nio.charset.CodingErrorAction.REPLACE)
decoder.replaceWith("\uFFFD")
return try {
decoder.decode(java.nio.ByteBuffer.wrap(bytes)).toString()
} catch (e: Exception) {
// Fallback to standard UTF-8 (shouldn't happen with REPLACE action)
String(bytes, Charsets.UTF_8)
}
}
/**
* 🔥 Расшифровывает reply blob из attachments (как в React Native)
@@ -842,8 +867,10 @@ object MessageCrypto {
val ciphertext = Base64.decode(parts[1], Base64.DEFAULT)
android.util.Log.d("MessageCrypto", " - IV size: ${iv.size}, Ciphertext size: ${ciphertext.size}")
// Password from keyAndNonce
val password = String(plainKeyAndNonce, Charsets.UTF_8)
// Password from keyAndNonce - use same JS-like UTF-8 conversion
val password = bytesToJsUtf8String(plainKeyAndNonce)
android.util.Log.d("MessageCrypto", " - Password length: ${password.length}")
android.util.Log.d("MessageCrypto", " - Password char codes: ${password.take(5).map { it.code }}")
// PBKDF2 key derivation
val factory = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
@@ -855,6 +882,7 @@ object MessageCrypto {
)
val secretKey = factory.generateSecret(spec)
val keyBytes = secretKey.encoded
android.util.Log.d("MessageCrypto", " - PBKDF2 key derived: ${keyBytes.toHex()}")
// AES-CBC decryption
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")

View File

@@ -224,11 +224,18 @@ fun ChatDetailScreen(
var showEmojiPicker by remember { mutableStateOf(false) }
val emojiPanelHeight = if (imeHeight > 50.dp) imeHeight else 280.dp
// Динамический bottom padding для списка: инпут (~70dp) + клавиатура/эмодзи
// 🔥 Reply/Forward state (нужен для расчёта listBottomPadding)
val replyMessages by viewModel.replyMessages.collectAsState()
val hasReply = replyMessages.isNotEmpty()
// 🔥 Дополнительная высота для reply панели (~50dp)
val replyPanelHeight = if (hasReply) 50.dp else 0.dp
// Динамический bottom padding для списка: инпут (~70dp) + reply (~50dp) + клавиатура/эмодзи
val listBottomPadding = when {
isKeyboardVisible -> 70.dp + imeHeight
showEmojiPicker -> 70.dp + emojiPanelHeight
else -> 100.dp
isKeyboardVisible -> 70.dp + replyPanelHeight + imeHeight
showEmojiPicker -> 70.dp + replyPanelHeight + emojiPanelHeight
else -> 100.dp + replyPanelHeight
}
// Telegram-style scroll tracking
@@ -289,10 +296,8 @@ fun ChatDetailScreen(
val isTyping by viewModel.opponentTyping.collectAsState()
val isOnline by viewModel.opponentOnline.collectAsState()
// 🔥 Reply/Forward state
val replyMessages by viewModel.replyMessages.collectAsState()
// 🔥 Reply/Forward state (replyMessages и hasReply определены выше для listBottomPadding)
val isForwardMode by viewModel.isForwardMode.collectAsState()
val hasReply = replyMessages.isNotEmpty()
// 🔥 Добавляем информацию о датах к сообщениям
// В reversed layout (новые внизу) - показываем дату ПОСЛЕ сообщения (визуально сверху)

View File

@@ -801,6 +801,13 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
attachments = messageAttachments
}
// 🔥 Log packet details before sending
ProtocolManager.addLog("📤📤📤 SENDING PACKET 📤📤📤")
ProtocolManager.addLog(" - Attachments count: ${packet.attachments.size}")
packet.attachments.forEach { att ->
ProtocolManager.addLog(" - Attachment: type=${att.type}, id=${att.id}, blob=${att.blob.take(50)}...")
}
// Отправляем пакет
ProtocolManager.send(packet)