From e9dece8e86ba75444a9d1acdfbc02b998980eb18 Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Wed, 14 Jan 2026 03:44:29 +0500 Subject: [PATCH] feat: Remove debug logging from message sending and user blocking/unblocking functions --- .../rosetta/messenger/crypto/CryptoManager.kt | 1 - .../rosetta/messenger/crypto/MessageCrypto.kt | 181 ------------------ .../rosetta/messenger/data/AccountManager.kt | 6 - .../messenger/data/MessageRepository.kt | 32 ---- .../messenger/ui/chats/ChatDetailScreen.kt | 17 +- .../messenger/ui/chats/ChatViewModel.kt | 15 -- .../messenger/ui/chats/ChatsListViewModel.kt | 6 - 7 files changed, 3 insertions(+), 255 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/crypto/CryptoManager.kt b/app/src/main/java/com/rosetta/messenger/crypto/CryptoManager.kt index 6e3dbc9..e42c031 100644 --- a/app/src/main/java/com/rosetta/messenger/crypto/CryptoManager.kt +++ b/app/src/main/java/com/rosetta/messenger/crypto/CryptoManager.kt @@ -211,7 +211,6 @@ object CryptoManager { // Decompress (zlib inflate - совместимо с pako.inflate в JS) String(decompress(decrypted), Charsets.UTF_8) } catch (e: Exception) { - android.util.Log.e("CryptoManager", "decryptWithPassword failed: ${e.message}") null } } diff --git a/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt b/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt index d8622b3..31fd23e 100644 --- a/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt +++ b/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt @@ -77,20 +77,14 @@ object MessageCrypto { * XChaCha20-Poly1305 encrypt implementation */ private fun xchacha20Poly1305Encrypt(plaintext: ByteArray, key: ByteArray, nonce: ByteArray): ByteArray { - android.util.Log.d("MessageCrypto", "🔐 XChaCha20-Poly1305 Encrypt:") - android.util.Log.d("MessageCrypto", " Key hex: ${key.toHex()}") - android.util.Log.d("MessageCrypto", " Nonce hex: ${nonce.toHex()}") - android.util.Log.d("MessageCrypto", " Plaintext hex: ${plaintext.toHex()}") // Step 1: Derive subkey using HChaCha20 val subkey = hchacha20(key, nonce.copyOfRange(0, 16)) - android.util.Log.d("MessageCrypto", " Subkey hex: ${subkey.toHex()}") // Step 2: Create ChaCha20 nonce (4 zeros + last 8 bytes of original nonce) val chacha20Nonce = ByteArray(12) // First 4 bytes are 0 System.arraycopy(nonce, 16, chacha20Nonce, 4, 8) - android.util.Log.d("MessageCrypto", " ChaCha20 nonce hex: ${chacha20Nonce.toHex()}") // Step 3: Initialize ChaCha20 engine ONCE val engine = ChaCha7539Engine() @@ -99,14 +93,11 @@ object MessageCrypto { // Step 4: Generate Poly1305 key from first 64 bytes of keystream (counter=0) val poly1305KeyBlock = ByteArray(64) engine.processBytes(ByteArray(64), 0, 64, poly1305KeyBlock, 0) - android.util.Log.d("MessageCrypto", " Poly1305 key hex: ${poly1305KeyBlock.copyOfRange(0, 32).toHex()}") // Step 5: Encrypt plaintext (engine continues from counter=1 automatically) val ciphertext = ByteArray(plaintext.size) engine.processBytes(plaintext, 0, plaintext.size, ciphertext, 0) - android.util.Log.d("MessageCrypto", " Ciphertext hex: ${ciphertext.toHex()}") - android.util.Log.d("MessageCrypto", " Ciphertext hex: ${ciphertext.toHex()}") // Step 6: Generate Poly1305 tag val mac = Poly1305() @@ -309,15 +300,11 @@ object MessageCrypto { * КРИТИЧНО: ephemeralKey.getPrivate('hex') в JS может быть БЕЗ ведущих нулей! */ fun encryptKeyForRecipient(keyAndNonce: ByteArray, recipientPublicKeyHex: String): String { - android.util.Log.d("MessageCrypto", "\n" + "━".repeat(80)) - android.util.Log.d("MessageCrypto", "🔐 ECDH ENCRYPT KEY FOR RECIPIENT") - android.util.Log.d("MessageCrypto", "━".repeat(80)) val secureRandom = SecureRandom() val ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1") // Генерируем эфемерный приватный ключ (32 байта) - android.util.Log.d("MessageCrypto", "📍 Step 1: Generate ephemeral key pair") val ephemeralPrivateKeyBytes = ByteArray(32) secureRandom.nextBytes(ephemeralPrivateKeyBytes) val ephemeralPrivateKey = BigInteger(1, ephemeralPrivateKeyBytes) @@ -328,29 +315,20 @@ object MessageCrypto { val ephemeralPrivateKeyHex = ephemeralPrivateKey.toString(16).let { // Но если нечётная длина, добавим ведущий 0 для правильного парсинга if (it.length % 2 != 0) { - android.util.Log.d("MessageCrypto", "⚠️ Padded ephemeral private key (was odd length)") "0$it" } else it } - android.util.Log.d("MessageCrypto", " ✓ Ephemeral private key: $ephemeralPrivateKeyHex") - android.util.Log.d("MessageCrypto", " ✓ Ephemeral private length: ${ephemeralPrivateKeyHex.length} hex chars") // Получаем эфемерный публичный ключ val ephemeralPublicKey = ecSpec.g.multiply(ephemeralPrivateKey) val ephemeralPublicKeyHex = ephemeralPublicKey.getEncoded(false).toHex() - android.util.Log.d("MessageCrypto", " ✓ Ephemeral public key: $ephemeralPublicKeyHex") // Парсим публичный ключ получателя - android.util.Log.d("MessageCrypto", "\n📍 Step 2: Parse recipient public key") - android.util.Log.d("MessageCrypto", " ✓ Recipient public key: $recipientPublicKeyHex") val recipientPublicKeyBytes = recipientPublicKeyHex.hexToBytes() - android.util.Log.d("MessageCrypto", " ✓ Recipient key bytes: ${recipientPublicKeyBytes.size}") val recipientPublicKey = ecSpec.curve.decodePoint(recipientPublicKeyBytes) // ECDH: ephemeralPrivate * recipientPublic = sharedSecret - android.util.Log.d("MessageCrypto", "\n📍 Step 3: Compute ECDH shared secret") - android.util.Log.d("MessageCrypto", " • Computing: ephemeral_private × recipient_public") val sharedPoint = recipientPublicKey.multiply(ephemeralPrivateKey) // ⚠️ КРИТИЧНО: Эмулируем JS поведение! @@ -358,48 +336,31 @@ object MessageCrypto { // crypto.enc.Hex.parse(hex) парсит как есть // Если X coordinate = 0x00abc..., JS получит "abc..." (меньше 32 байт) val xCoordBigInt = sharedPoint.normalize().xCoord.toBigInteger() - android.util.Log.d("MessageCrypto", " • X coordinate (BigInt): $xCoordBigInt") var sharedSecretHex = xCoordBigInt.toString(16) - android.util.Log.d("MessageCrypto", " • toString(16) result: $sharedSecretHex (${sharedSecretHex.length} chars)") // JS: если hex нечётной длины, crypto.enc.Hex.parse добавит ведущий 0 if (sharedSecretHex.length % 2 != 0) { - android.util.Log.d("MessageCrypto", " ⚠️ Padding to even length for hex parsing") sharedSecretHex = "0$sharedSecretHex" } val sharedSecret = sharedSecretHex.hexToBytes() - android.util.Log.d("MessageCrypto", " ✓ SHARED SECRET hex: $sharedSecretHex") - android.util.Log.d("MessageCrypto", " ✓ Shared secret bytes: ${sharedSecret.size} bytes") // Генерируем IV для AES (16 байт) - android.util.Log.d("MessageCrypto", "\n📍 Step 4: Generate AES IV") val iv = ByteArray(16) secureRandom.nextBytes(iv) val ivHex = iv.toHex() - android.util.Log.d("MessageCrypto", " ✓ IV: $ivHex") // ⚠️ КРИТИЧНО: Эмулируем поведение React Native + crypto-js! // React Native: Buffer.toString('binary') → строка с символами (включая > 127) // crypto-js: AES.encrypt(string, ...) → кодирует строку в UTF-8 перед шифрованием // Итого: байты > 127 превращаются в многобайтовые UTF-8 последовательности - android.util.Log.d("MessageCrypto", "\n📍 Step 5: Latin1 → UTF-8 (crypto-js compatibility)") - android.util.Log.d("MessageCrypto", " • Input key+nonce bytes: ${keyAndNonce.size}") - android.util.Log.d("MessageCrypto", " • Input hex: ${keyAndNonce.toHex()}") // Шаг 1: Байты → Latin1 строка (как Buffer.toString('binary')) val latin1String = String(keyAndNonce, Charsets.ISO_8859_1) - android.util.Log.d("MessageCrypto", " • Latin1 string length: ${latin1String.length} chars") // Шаг 2: Latin1 строка → UTF-8 байты (как crypto-js делает внутри) val utf8Bytes = latin1String.toByteArray(Charsets.UTF_8) - android.util.Log.d("MessageCrypto", " ✓ UTF-8 bytes length: ${utf8Bytes.size}") - android.util.Log.d("MessageCrypto", " ✓ UTF-8 hex: ${utf8Bytes.toHex()}") // AES шифрование - android.util.Log.d("MessageCrypto", "\n📍 Step 6: AES-CBC encryption") - android.util.Log.d("MessageCrypto", " • AES key (shared secret): ${sharedSecret.size} bytes") - android.util.Log.d("MessageCrypto", " • IV: ${iv.size} bytes") - android.util.Log.d("MessageCrypto", " • Plaintext: ${utf8Bytes.size} bytes") val aesKey = SecretKeySpec(sharedSecret, "AES") val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") @@ -407,19 +368,11 @@ object MessageCrypto { val encryptedKey = cipher.doFinal(utf8Bytes) val encryptedKeyHex = encryptedKey.toHex() - android.util.Log.d("MessageCrypto", " ✓ CIPHERTEXT: $encryptedKeyHex") - android.util.Log.d("MessageCrypto", " ✓ Ciphertext length: ${encryptedKey.size} bytes") // Формат как в RN: btoa(ivHex:encryptedHex:ephemeralPrivateHex) - android.util.Log.d("MessageCrypto", "\n📍 Step 7: Format as iv:ciphertext:ephemeralPrivate") val combined = "$ivHex:$encryptedKeyHex:$ephemeralPrivateKeyHex" - android.util.Log.d("MessageCrypto", " • Combined string: ${combined.take(160)}...") - android.util.Log.d("MessageCrypto", " • Combined length: ${combined.length} chars") val result = Base64.encodeToString(combined.toByteArray(), Base64.NO_WRAP) - android.util.Log.d("MessageCrypto", "\n ✅ FINAL ENCRYPTED KEY (Base64): $result") - android.util.Log.d("MessageCrypto", " ✅ Base64 length: ${result.length} chars") - android.util.Log.d("MessageCrypto", "━".repeat(80) + "\n") return result } @@ -430,111 +383,68 @@ object MessageCrypto { * КРИТИЧНО: ephemeralPrivateKeyHex может иметь нечётную длину! */ fun decryptKeyFromSender(encryptedKeyBase64: String, myPrivateKeyHex: String): ByteArray { - android.util.Log.d("MessageCrypto", "\n" + "━".repeat(80)) - android.util.Log.d("MessageCrypto", "🔓 ECDH DECRYPT KEY FROM SENDER") - android.util.Log.d("MessageCrypto", "━".repeat(80)) - android.util.Log.d("MessageCrypto", "📥 Encrypted key (Base64): $encryptedKeyBase64") - android.util.Log.d("MessageCrypto", "📥 Base64 length: ${encryptedKeyBase64.length} chars") - android.util.Log.d("MessageCrypto", "🔑 My private key: $myPrivateKeyHex") - android.util.Log.d("MessageCrypto", "\n📍 Step 1: Decode Base64 and parse format") val combined = String(Base64.decode(encryptedKeyBase64, Base64.NO_WRAP)) - android.util.Log.d("MessageCrypto", " • Decoded string: ${combined.take(160)}...") - android.util.Log.d("MessageCrypto", " • Decoded length: ${combined.length} chars") val parts = combined.split(":") if (parts.size != 3) { - android.util.Log.e("MessageCrypto", "❌ Invalid format: expected 3 parts, got ${parts.size}") throw IllegalArgumentException("Invalid encrypted key format: expected 3 parts, got ${parts.size}") } - android.util.Log.d("MessageCrypto", " ✓ Parsed into 3 parts") val ivHex = parts[0] val encryptedKeyHex = parts[1] var ephemeralPrivateKeyHex = parts[2] - android.util.Log.d("MessageCrypto", " • Part 1 (IV): $ivHex (${ivHex.length} chars)") - android.util.Log.d("MessageCrypto", " • Part 2 (ciphertext): ${encryptedKeyHex.take(80)}... (${encryptedKeyHex.length} chars)") - android.util.Log.d("MessageCrypto", " • Part 3 (ephemeral private): $ephemeralPrivateKeyHex") // ⚠️ КРИТИЧНО: JS toString(16) может вернуть hex нечётной длины! // Добавляем ведущий 0 если нужно для правильного парсинга if (ephemeralPrivateKeyHex.length % 2 != 0) { - android.util.Log.d("MessageCrypto", " ⚠️ Padding ephemeral key from ${ephemeralPrivateKeyHex.length} to ${ephemeralPrivateKeyHex.length + 1}") ephemeralPrivateKeyHex = "0$ephemeralPrivateKeyHex" } val iv = ivHex.hexToBytes() val encryptedKey = encryptedKeyHex.hexToBytes() - android.util.Log.d("MessageCrypto", " ✓ IV bytes: ${iv.size}") - android.util.Log.d("MessageCrypto", " ✓ Ciphertext bytes: ${encryptedKey.size}") val ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1") // Парсим эфемерный приватный ключ - android.util.Log.d("MessageCrypto", "\n📍 Step 2: Parse ephemeral key pair") val ephemeralPrivateKey = BigInteger(ephemeralPrivateKeyHex, 16) val ephemeralPublicKey = ecSpec.g.multiply(ephemeralPrivateKey) val ephemeralPublicKeyHex = ephemeralPublicKey.getEncoded(false).toHex() - android.util.Log.d("MessageCrypto", " ✓ Ephemeral private: $ephemeralPrivateKeyHex") - android.util.Log.d("MessageCrypto", " ✓ Ephemeral public: $ephemeralPublicKeyHex") // Парсим мой приватный ключ - android.util.Log.d("MessageCrypto", "\n📍 Step 3: Parse my key pair") val myPrivateKey = BigInteger(myPrivateKeyHex, 16) val myPublicKey = ecSpec.g.multiply(myPrivateKey) val myPublicKeyHex = myPublicKey.getEncoded(false).toHex() - android.util.Log.d("MessageCrypto", " ✓ My private: ${myPrivateKeyHex.take(32)}...") - android.util.Log.d("MessageCrypto", " ✓ My public: $myPublicKeyHex") // ECDH: ephemeralPrivate * myPublic = sharedSecret - android.util.Log.d("MessageCrypto", "\n📍 Step 4: Compute ECDH shared secret") - android.util.Log.d("MessageCrypto", " • Computing: ephemeral_private × my_public") val sharedPoint = myPublicKey.multiply(ephemeralPrivateKey) // ⚠️ КРИТИЧНО: Эмулируем JS поведение! // JS: BN.toString(16) НЕ добавляет ведущие нули val xCoordBigInt = sharedPoint.normalize().xCoord.toBigInteger() - android.util.Log.d("MessageCrypto", " • X coordinate (BigInt): $xCoordBigInt") var sharedSecretHex = xCoordBigInt.toString(16) - android.util.Log.d("MessageCrypto", " • toString(16) result: $sharedSecretHex (${sharedSecretHex.length} chars)") if (sharedSecretHex.length % 2 != 0) { - android.util.Log.d("MessageCrypto", " ⚠️ Padding to even length") sharedSecretHex = "0$sharedSecretHex" } val sharedSecret = sharedSecretHex.hexToBytes() - android.util.Log.d("MessageCrypto", " ✓ SHARED SECRET hex: $sharedSecretHex") - android.util.Log.d("MessageCrypto", " ✓ Shared secret bytes: ${sharedSecret.size} bytes") // Расшифровываем используя sharedSecret как AES ключ - android.util.Log.d("MessageCrypto", "\n📍 Step 5: AES-CBC decryption") - android.util.Log.d("MessageCrypto", " • AES key (shared secret): ${sharedSecret.size} bytes") - android.util.Log.d("MessageCrypto", " • IV: ${iv.size} bytes") - android.util.Log.d("MessageCrypto", " • Ciphertext: ${encryptedKey.size} bytes") val aesKey = SecretKeySpec(sharedSecret, "AES") val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") cipher.init(Cipher.DECRYPT_MODE, aesKey, IvParameterSpec(iv)) val decryptedUtf8Bytes = cipher.doFinal(encryptedKey) - android.util.Log.d("MessageCrypto", " ✓ Decrypted UTF-8 bytes: ${decryptedUtf8Bytes.size}") - android.util.Log.d("MessageCrypto", " ✓ Decrypted hex: ${decryptedUtf8Bytes.toHex()}") // ⚠️ КРИТИЧНО: Обратная конвертация UTF-8 → Latin1! // Desktop: decrypted.toString(crypto.enc.Utf8) → Buffer.from(str, 'binary') // Это декодирует UTF-8 в строку, потом берёт charCode каждого символа - android.util.Log.d("MessageCrypto", "\n📍 Step 6: UTF-8 → Latin1 (crypto-js compatibility)") val utf8String = String(decryptedUtf8Bytes, Charsets.UTF_8) - android.util.Log.d("MessageCrypto", " • UTF-8 string length: ${utf8String.length} chars") val originalBytes = utf8String.toByteArray(Charsets.ISO_8859_1) - android.util.Log.d("MessageCrypto", " ✓ RESULT bytes: ${originalBytes.size}") - android.util.Log.d("MessageCrypto", " ✓ Result hex: ${originalBytes.toHex()}") - android.util.Log.d("MessageCrypto", "\n ✅ DECRYPTION COMPLETE") - android.util.Log.d("MessageCrypto", " ✅ Returned ${originalBytes.size} bytes") - android.util.Log.d("MessageCrypto", "━".repeat(80) + "\n") return originalBytes } @@ -552,51 +462,16 @@ object MessageCrypto { * Полное шифрование сообщения для отправки */ fun encryptForSending(plaintext: String, recipientPublicKey: String): EncryptedForSending { - android.util.Log.d("MessageCrypto", "=".repeat(100)) - android.util.Log.d("MessageCrypto", "🚀🚀🚀 START ENCRYPTION FOR SENDING 🚀🚀🚀") - android.util.Log.d("MessageCrypto", "=".repeat(100)) - android.util.Log.d("MessageCrypto", "📝 PLAINTEXT: '$plaintext'") - android.util.Log.d("MessageCrypto", "📝 Plaintext length: ${plaintext.length} chars") - android.util.Log.d("MessageCrypto", "📝 Plaintext bytes: ${plaintext.toByteArray().size} bytes") - android.util.Log.d("MessageCrypto", "📝 Plaintext hex (first 128): ${plaintext.toByteArray().take(64).toByteArray().toHex()}") - android.util.Log.d("MessageCrypto", "🔑 RECIPIENT PUBLIC KEY: $recipientPublicKey") - android.util.Log.d("MessageCrypto", "🔑 Public key length: ${recipientPublicKey.length} chars") // 1. Шифруем текст - android.util.Log.d("MessageCrypto", "\n" + "─".repeat(80)) - android.util.Log.d("MessageCrypto", "⚡ STEP 1: XCHACHA20-POLY1305 ENCRYPTION") - android.util.Log.d("MessageCrypto", "─".repeat(80)) val encrypted = encryptMessage(plaintext) - android.util.Log.d("MessageCrypto", "✅ CIPHERTEXT (hex): ${encrypted.ciphertext}") - android.util.Log.d("MessageCrypto", "✅ Ciphertext length: ${encrypted.ciphertext.length} chars") - android.util.Log.d("MessageCrypto", "✅ KEY (hex): ${encrypted.key}") - android.util.Log.d("MessageCrypto", "✅ Key length: ${encrypted.key.length} chars (should be 64)") - android.util.Log.d("MessageCrypto", "✅ NONCE (hex): ${encrypted.nonce}") - android.util.Log.d("MessageCrypto", "✅ Nonce length: ${encrypted.nonce.length} chars (should be 48)") // 2. Собираем key + nonce - android.util.Log.d("MessageCrypto", "\n" + "─".repeat(80)) - android.util.Log.d("MessageCrypto", "⚡ STEP 2: COMBINE KEY + NONCE") - android.util.Log.d("MessageCrypto", "─".repeat(80)) val keyAndNonce = encrypted.key.hexToBytes() + encrypted.nonce.hexToBytes() - android.util.Log.d("MessageCrypto", "✅ KEY+NONCE combined: ${keyAndNonce.size} bytes (should be 56)") - android.util.Log.d("MessageCrypto", "✅ Combined hex: ${keyAndNonce.toHex()}") // 3. Шифруем ключ для получателя - android.util.Log.d("MessageCrypto", "\n" + "─".repeat(80)) - android.util.Log.d("MessageCrypto", "⚡ STEP 3: ECDH + AES ENCRYPTION OF KEY") - android.util.Log.d("MessageCrypto", "─".repeat(80)) val encryptedKey = encryptKeyForRecipient(keyAndNonce, recipientPublicKey) - android.util.Log.d("MessageCrypto", "✅ ENCRYPTED KEY (Base64): $encryptedKey") - android.util.Log.d("MessageCrypto", "✅ Encrypted key length: ${encryptedKey.length} chars") - android.util.Log.d("MessageCrypto", "\n" + "=".repeat(100)) - android.util.Log.d("MessageCrypto", "✅✅✅ ENCRYPTION COMPLETE ✅✅✅") - android.util.Log.d("MessageCrypto", "=".repeat(100)) - android.util.Log.d("MessageCrypto", "FINAL OUTPUTS:") - android.util.Log.d("MessageCrypto", " • Ciphertext: ${encrypted.ciphertext.take(60)}... (${encrypted.ciphertext.length} chars)") - android.util.Log.d("MessageCrypto", " • Encrypted key: ${encryptedKey.take(60)}... (${encryptedKey.length} chars)") - android.util.Log.d("MessageCrypto", "=".repeat(100) + "\n") return EncryptedForSending(encrypted.ciphertext, encryptedKey, keyAndNonce) } @@ -610,48 +485,17 @@ object MessageCrypto { encryptedKey: String, myPrivateKey: String ): DecryptedIncoming { - android.util.Log.d("MessageCrypto", "=".repeat(100)) - android.util.Log.d("MessageCrypto", "🔓🔓🔓 START DECRYPTION OF INCOMING MESSAGE 🔓🔓🔓") - android.util.Log.d("MessageCrypto", "=".repeat(100)) - android.util.Log.d("MessageCrypto", "📩 CIPHERTEXT (hex): ${ciphertext.take(100)}...") - android.util.Log.d("MessageCrypto", "📩 Ciphertext length: ${ciphertext.length} chars") - android.util.Log.d("MessageCrypto", "🔐 ENCRYPTED KEY: ${encryptedKey.take(100)}...") - android.util.Log.d("MessageCrypto", "🔐 Encrypted key length: ${encryptedKey.length} chars") - android.util.Log.d("MessageCrypto", "🔑 MY PRIVATE KEY: ${myPrivateKey.take(32)}... (${myPrivateKey.length} chars)") // 1. Расшифровываем ключ - android.util.Log.d("MessageCrypto", "\n" + "─".repeat(80)) - android.util.Log.d("MessageCrypto", "⚡ STEP 1: ECDH + AES DECRYPTION OF KEY") - android.util.Log.d("MessageCrypto", "─".repeat(80)) val keyAndNonce = decryptKeyFromSender(encryptedKey, myPrivateKey) - android.util.Log.d("MessageCrypto", "✅ DECRYPTED KEY+NONCE: ${keyAndNonce.size} bytes (should be 56)") - android.util.Log.d("MessageCrypto", "✅ Key+Nonce hex: ${keyAndNonce.toHex()}") // 2. Разделяем key и nonce - android.util.Log.d("MessageCrypto", "\n" + "─".repeat(80)) - android.util.Log.d("MessageCrypto", "⚡ STEP 2: SPLIT KEY AND NONCE") - android.util.Log.d("MessageCrypto", "─".repeat(80)) val key = keyAndNonce.slice(0 until 32).toByteArray() val nonce = keyAndNonce.slice(32 until keyAndNonce.size).toByteArray() - android.util.Log.d("MessageCrypto", "✅ KEY (32 bytes): ${key.toHex()}") - android.util.Log.d("MessageCrypto", "✅ NONCE (24 bytes): ${nonce.toHex()}") - android.util.Log.d("MessageCrypto", "✅ Key length: ${key.size} bytes") - android.util.Log.d("MessageCrypto", "✅ Nonce length: ${nonce.size} bytes") // 3. Расшифровываем сообщение - android.util.Log.d("MessageCrypto", "\n" + "─".repeat(80)) - android.util.Log.d("MessageCrypto", "⚡ STEP 3: XCHACHA20-POLY1305 DECRYPTION") - android.util.Log.d("MessageCrypto", "─".repeat(80)) val plaintext = decryptMessage(ciphertext, key.toHex(), nonce.toHex()) - android.util.Log.d("MessageCrypto", "✅ PLAINTEXT: '$plaintext'") - android.util.Log.d("MessageCrypto", "✅ Plaintext length: ${plaintext.length} chars") - android.util.Log.d("MessageCrypto", "✅ Plaintext bytes: ${plaintext.toByteArray().size} bytes") - android.util.Log.d("MessageCrypto", "\n" + "=".repeat(100)) - android.util.Log.d("MessageCrypto", "✅✅✅ DECRYPTION COMPLETE ✅✅✅") - android.util.Log.d("MessageCrypto", "=".repeat(100)) - android.util.Log.d("MessageCrypto", "FINAL OUTPUT: '$plaintext'") - android.util.Log.d("MessageCrypto", "=".repeat(100) + "\n") return DecryptedIncoming(plaintext, keyAndNonce) } @@ -690,7 +534,6 @@ object MessageCrypto { // 4. Расшифровываем AES-256-CBC decryptWithPBKDF2Key(encryptedData, pbkdf2Key) } catch (e: Exception) { - android.util.Log.e("MessageCrypto", "❌ Failed to decrypt attachment blob", e) null } } @@ -718,7 +561,6 @@ object MessageCrypto { return try { val parts = encryptedData.split(":") if (parts.size != 2) { - android.util.Log.e("MessageCrypto", "Invalid encrypted data format") return null } @@ -745,7 +587,6 @@ object MessageCrypto { String(outputStream.toByteArray(), Charsets.UTF_8) } catch (e: Exception) { - android.util.Log.e("MessageCrypto", "❌ Failed to decrypt with PBKDF2 key", e) null } } @@ -765,15 +606,10 @@ object MessageCrypto { */ fun encryptReplyBlob(replyJson: String, plainKeyAndNonce: ByteArray): String { return try { - android.util.Log.d("MessageCrypto", "🔐 Encrypting reply blob...") - android.util.Log.d("MessageCrypto", " - ReplyJson length: ${replyJson.length}") - android.util.Log.d("MessageCrypto", " - PlainKeyAndNonce length: ${plainKeyAndNonce.size}") // 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() @@ -783,7 +619,6 @@ object MessageCrypto { val compressedSize = deflater.deflate(compressedBuffer) deflater.end() val compressed = compressedBuffer.copyOf(compressedSize) - android.util.Log.d("MessageCrypto", " - Compressed size: ${compressed.size}") // PBKDF2 key derivation (matching RN: crypto.PBKDF2(password, 'rosetta', {keySize: 256/32, iterations: 1000})) val factory = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1") @@ -795,12 +630,10 @@ object MessageCrypto { ) val secretKey = factory.generateSecret(spec) val keyBytes = secretKey.encoded - android.util.Log.d("MessageCrypto", " - PBKDF2 key derived: ${keyBytes.toHex()}") // Generate random IV (16 bytes) val iv = ByteArray(16) java.security.SecureRandom().nextBytes(iv) - android.util.Log.d("MessageCrypto", " - IV generated: ${iv.size} bytes") // AES-CBC encryption val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") @@ -808,17 +641,14 @@ object MessageCrypto { val ivSpec = IvParameterSpec(iv) cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec) val ciphertext = cipher.doFinal(compressed) - android.util.Log.d("MessageCrypto", " - Ciphertext size: ${ciphertext.size}") // Format: "ivBase64:ciphertextBase64" (same as RN new format) val ivBase64 = Base64.encodeToString(iv, Base64.NO_WRAP) val ctBase64 = Base64.encodeToString(ciphertext, Base64.NO_WRAP) val result = "$ivBase64:$ctBase64" - android.util.Log.d("MessageCrypto", " ✅ Reply blob encrypted: ${result.take(50)}...") result } catch (e: Exception) { - android.util.Log.e("MessageCrypto", "❌ Failed to encrypt reply blob", e) // Fallback: return plaintext (for backwards compatibility) replyJson } @@ -849,31 +679,23 @@ object MessageCrypto { */ fun decryptReplyBlob(encryptedBlob: String, plainKeyAndNonce: ByteArray): String { return try { - android.util.Log.d("MessageCrypto", "🔓 Decrypting reply blob...") - android.util.Log.d("MessageCrypto", " - EncryptedBlob length: ${encryptedBlob.length}") - android.util.Log.d("MessageCrypto", " - PlainKeyAndNonce length: ${plainKeyAndNonce.size}") // Check if it's encrypted format (contains ':') if (!encryptedBlob.contains(':')) { - android.util.Log.d("MessageCrypto", " - Plain JSON detected, returning as-is") return encryptedBlob } // Parse ivBase64:ciphertextBase64 val parts = encryptedBlob.split(':') if (parts.size != 2) { - android.util.Log.e("MessageCrypto", " - Invalid format, expected iv:ciphertext") return encryptedBlob } val iv = Base64.decode(parts[0], Base64.DEFAULT) val ciphertext = Base64.decode(parts[1], Base64.DEFAULT) - android.util.Log.d("MessageCrypto", " - IV size: ${iv.size}, Ciphertext size: ${ciphertext.size}") // 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") @@ -885,7 +707,6 @@ 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") @@ -902,10 +723,8 @@ object MessageCrypto { inflater.end() val plaintext = String(outputBuffer, 0, outputSize, Charsets.UTF_8) - android.util.Log.d("MessageCrypto", " ✅ Reply blob decrypted: ${plaintext.take(50)}...") plaintext } catch (e: Exception) { - android.util.Log.e("MessageCrypto", "❌ Failed to decrypt reply blob: ${e.message}", e) // Return as-is, might be plain JSON encryptedBlob } diff --git a/app/src/main/java/com/rosetta/messenger/data/AccountManager.kt b/app/src/main/java/com/rosetta/messenger/data/AccountManager.kt index 0154d9c..b6bfc63 100644 --- a/app/src/main/java/com/rosetta/messenger/data/AccountManager.kt +++ b/app/src/main/java/com/rosetta/messenger/data/AccountManager.kt @@ -41,19 +41,15 @@ class AccountManager(private val context: Context) { // Synchronous read from SharedPreferences - always up to date fun getLastLoggedPublicKey(): String? { val publicKey = sharedPrefs.getString(KEY_LAST_LOGGED, null) - android.util.Log.d("AccountManager", "📖 getLastLoggedPublicKey: ${publicKey?.take(16) ?: "null"}") return publicKey } // Synchronous write to SharedPreferences fun setLastLoggedPublicKey(publicKey: String) { - android.util.Log.d("AccountManager", "💾 Saving last logged account: ${publicKey.take(16)}...") val success = sharedPrefs.edit().putString(KEY_LAST_LOGGED, publicKey).commit() // commit() is synchronous - android.util.Log.d("AccountManager", "💾 Save result: $success") // Verify immediately val saved = sharedPrefs.getString(KEY_LAST_LOGGED, null) - android.util.Log.d("AccountManager", "💾 Verification read: ${saved?.take(16) ?: "null"}") } suspend fun saveAccount(account: EncryptedAccount) { @@ -86,7 +82,6 @@ class AccountManager(private val context: Context) { } suspend fun setCurrentAccount(publicKey: String) { - android.util.Log.d("AccountManager", "🔐 setCurrentAccount called for: ${publicKey.take(16)}...") // ⚡ ВАЖНО: Сначала сохраняем в SharedPreferences синхронно setLastLoggedPublicKey(publicKey) @@ -97,7 +92,6 @@ class AccountManager(private val context: Context) { preferences[IS_LOGGED_IN] = true } - android.util.Log.d("AccountManager", "✅ setCurrentAccount completed for: ${publicKey.take(16)}...") } suspend fun logout() { diff --git a/app/src/main/java/com/rosetta/messenger/data/MessageRepository.kt b/app/src/main/java/com/rosetta/messenger/data/MessageRepository.kt index 654682c..573d268 100644 --- a/app/src/main/java/com/rosetta/messenger/data/MessageRepository.kt +++ b/app/src/main/java/com/rosetta/messenger/data/MessageRepository.kt @@ -93,14 +93,12 @@ class MessageRepository private constructor(private val context: Context) { * Инициализация с текущим аккаунтом */ fun initialize(publicKey: String, privateKey: String) { - android.util.Log.d("MessageRepository", "🔐 initialize() called with publicKey: ${publicKey.take(16)}...") currentAccount = publicKey currentPrivateKey = privateKey // Загрузка диалогов scope.launch { dialogDao.getDialogsFlow(publicKey).collect { entities -> - android.util.Log.d("MessageRepository", "📋 MessageRepository dialogsFlow emitted: ${entities.size} dialogs") _dialogs.value = entities.map { it.toDialog() } // 🔥 Запрашиваем информацию о пользователях, у которых нет имени @@ -238,23 +236,17 @@ class MessageRepository private constructor(private val context: Context) { * Обработка входящего сообщения */ suspend fun handleIncomingMessage(packet: PacketMessage) { - android.util.Log.d("MessageRepository", "═══════════════════════════════════════") - android.util.Log.d("MessageRepository", "📩 handleIncomingMessage START") - android.util.Log.d("MessageRepository", " from: ${packet.fromPublicKey.take(20)}...") val account = currentAccount ?: run { - android.util.Log.e("MessageRepository", "❌ ABORT: currentAccount is NULL!") return } val privateKey = currentPrivateKey ?: run { - android.util.Log.e("MessageRepository", "❌ ABORT: currentPrivateKey is NULL!") return } // 🔥 Проверяем, не заблокирован ли отправитель val isBlocked = database.blacklistDao().isUserBlocked(packet.fromPublicKey, account) if (isBlocked) { - android.util.Log.d("MessageRepository", "🚫 BLOCKED: Ignoring message from blocked user ${packet.fromPublicKey.take(20)}...") return } @@ -264,20 +256,14 @@ class MessageRepository private constructor(private val context: Context) { } else { packet.messageId } - android.util.Log.d("MessageRepository", " messageId: $messageId (original: ${packet.messageId})") - android.util.Log.d("MessageRepository", " currentAccount: ${account.take(20)}...") - android.util.Log.d("MessageRepository", " currentPrivateKey: SET") // Проверяем, не дубликат ли (используем сгенерированный messageId) val isDuplicate = messageDao.messageExists(account, messageId) - android.util.Log.d("MessageRepository", " isDuplicate: $isDuplicate") if (isDuplicate) { - android.util.Log.d("MessageRepository", "⚠️ Skipping duplicate message") return } val dialogKey = getDialogKey(packet.fromPublicKey) - android.util.Log.d("MessageRepository", " dialogKey: $dialogKey") try { // Расшифровываем @@ -314,12 +300,9 @@ class MessageRepository private constructor(private val context: Context) { dialogKey = dialogKey ) messageDao.insertMessage(entity) - android.util.Log.d("MessageRepository", "✅ Message saved to DB: ${packet.messageId.take(16)}...") // Обновляем диалог - android.util.Log.d("MessageRepository", "🔄 Calling updateDialog for ${packet.fromPublicKey.take(16)}...") updateDialog(packet.fromPublicKey, plainText, packet.timestamp, incrementUnread = true) - android.util.Log.d("MessageRepository", "✅ updateDialog completed!") // 🔥 Запрашиваем информацию о пользователе для отображения имени вместо ключа requestUserInfo(packet.fromPublicKey) @@ -329,7 +312,6 @@ class MessageRepository private constructor(private val context: Context) { updateMessageCache(dialogKey, message) } catch (e: Exception) { - android.util.Log.e("MessageRepository", "❌ Error handling incoming message", e) e.printStackTrace() } } @@ -382,7 +364,6 @@ class MessageRepository private constructor(private val context: Context) { // чтобы unread_count обновился моментально dialogDao.updateDialogFromMessages(account, opponentKey) - android.util.Log.d("MessageRepository", "✅ Dialog marked as read and updated from messages") } /** @@ -469,13 +450,10 @@ class MessageRepository private constructor(private val context: Context) { val account = currentAccount ?: return val privateKey = currentPrivateKey ?: return - android.util.Log.d("MessageRepository", "📝 Updating dialog for ${opponentKey.take(16)}...") - android.util.Log.d("MessageRepository", " lastMessage: ${lastMessage.take(50)}") try { // 🔥 КРИТИЧНО: Сначала считаем реальное количество непрочитанных из messages val unreadCount = messageDao.getUnreadCountForDialog(account, opponentKey) - android.util.Log.d("MessageRepository", " unreadCount from messages: $unreadCount") // 🔒 Шифруем lastMessage val encryptedLastMessage = CryptoManager.encryptWithPassword(lastMessage, privateKey) @@ -485,12 +463,10 @@ class MessageRepository private constructor(private val context: Context) { if (existing != null) { // Обновляем существующий диалог - android.util.Log.d("MessageRepository", " ✏️ Updating existing dialog...") dialogDao.updateLastMessage(account, opponentKey, encryptedLastMessage, timestamp) dialogDao.updateUnreadCount(account, opponentKey, unreadCount) } else { // Создаем новый диалог - android.util.Log.d("MessageRepository", " ➕ Creating new dialog...") dialogDao.insertDialog(DialogEntity( account = account, opponentKey = opponentKey, @@ -500,9 +476,7 @@ class MessageRepository private constructor(private val context: Context) { )) } - android.util.Log.d("MessageRepository", " ✅ Dialog updated successfully!") } catch (e: Exception) { - android.util.Log.e("MessageRepository", " ❌ Error updating dialog", e) } } @@ -520,7 +494,6 @@ class MessageRepository private constructor(private val context: Context) { lastSeen = if (!isOnline) System.currentTimeMillis() else 0 ) - android.util.Log.d("MessageRepository", "🟢 Updated online status for ${publicKey.take(16)}... isOnline=$isOnline") } /** @@ -534,7 +507,6 @@ class MessageRepository private constructor(private val context: Context) { val existing = dialogDao.getDialog(account, publicKey) if (existing != null) { dialogDao.updateOpponentInfo(account, publicKey, title, username, verified) - android.util.Log.d("MessageRepository", "✅ Updated user info for ${publicKey.take(16)}... title=$title") } } @@ -551,7 +523,6 @@ class MessageRepository private constructor(private val context: Context) { this.search = publicKey } ProtocolManager.send(packet) - android.util.Log.d("MessageRepository", "📤 Requested user info for ${publicKey.take(16)}...") } } @@ -563,7 +534,6 @@ class MessageRepository private constructor(private val context: Context) { try { CryptoManager.decryptWithPassword(plainMessage, privateKey) ?: plainMessage } catch (e: Exception) { - android.util.Log.e("MessageRepository", "Failed to decrypt plainMessage: ${e.message}") plainMessage // Fallback на зашифрованный текст если расшифровка не удалась } } else { @@ -649,7 +619,6 @@ class MessageRepository private constructor(private val context: Context) { jsonObj.put("preview", decryptedBlob) // Для совместимости jsonObj.put("width", attachment.width) jsonObj.put("height", attachment.height) - android.util.Log.d("MessageRepository", "✅ Decrypted MESSAGES blob: ${decryptedBlob.take(100)}") } else { // Fallback - сохраняем как есть jsonObj.put("id", attachment.id) @@ -660,7 +629,6 @@ class MessageRepository private constructor(private val context: Context) { jsonObj.put("height", attachment.height) } } catch (e: Exception) { - android.util.Log.e("MessageRepository", "❌ Failed to decrypt MESSAGES blob", e) // Fallback - сохраняем как есть jsonObj.put("id", attachment.id) jsonObj.put("blob", attachment.blob) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt index 328ab53..11bbdce 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt @@ -911,13 +911,9 @@ fun ChatDetailScreen( } }, onSend = { - android.util.Log.d("ChatDetailScreen", "🔥🔥🔥 onSend callback CALLED 🔥🔥🔥") - android.util.Log.d("ChatDetailScreen", "📝 inputText: '$inputText'") // Скрываем кнопку scroll на время отправки isSendingMessage = true - android.util.Log.d("ChatDetailScreen", "➡️ Calling viewModel.sendMessage()") viewModel.sendMessage() - android.util.Log.d("ChatDetailScreen", "✅ viewModel.sendMessage() called") // Скроллим к новому сообщению scope.launch { delay(100) @@ -1322,9 +1318,8 @@ fun ChatDetailScreen( account = currentUserPublicKey, opponentKey = user.publicKey ) - android.util.Log.d("ChatDetail", "✅ Chat deleted with: ${user.publicKey.take(10)}") } catch (e: Exception) { - android.util.Log.e("ChatDetail", "❌ Error deleting chat", e) + // Error deleting chat } // Выходим ПОСЛЕ удаления onBack() @@ -1366,7 +1361,6 @@ fun ChatDetailScreen( showBlockConfirm = false scope.launch { try { - android.util.Log.d("ChatDetail", "🚫 Blocking user: ${user.publicKey.take(10)}") // Добавляем пользователя в blacklist database.blacklistDao().blockUser( com.rosetta.messenger.database.BlacklistEntity( @@ -1375,9 +1369,8 @@ fun ChatDetailScreen( ) ) isBlocked = true - android.util.Log.d("ChatDetail", "✅ User blocked: ${user.publicKey.take(10)}") } catch (e: Exception) { - android.util.Log.e("ChatDetail", "❌ Error blocking user", e) + // Error blocking user } } } @@ -1417,16 +1410,14 @@ fun ChatDetailScreen( showUnblockConfirm = false scope.launch { try { - android.util.Log.d("ChatDetail", "✅ Unblocking user: ${user.publicKey.take(10)}") // Удаляем пользователя из blacklist database.blacklistDao().unblockUser( publicKey = user.publicKey, account = currentUserPublicKey ) isBlocked = false - android.util.Log.d("ChatDetail", "✅ User unblocked: ${user.publicKey.take(10)}") } catch (e: Exception) { - android.util.Log.e("ChatDetail", "❌ Error unblocking user", e) + // Error unblocking user } } } @@ -2087,10 +2078,8 @@ private fun MessageInputBar( // Функция отправки - НЕ закрывает клавиатуру (UX правило #6) fun handleSend() { - android.util.Log.d("MessageInputBar", "🚀 handleSend() called, value='$value', isNotBlank=${value.isNotBlank()}, hasReply=$hasReply") // Можно отправить если есть текст ИЛИ есть reply (как в React Native) if (value.isNotBlank() || hasReply) { - android.util.Log.d("MessageInputBar", "✅ Calling onSend()") onSend() // Очищаем инпут, но клавиатура остаётся открытой } diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatViewModel.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatViewModel.kt index d16993a..44222b8 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatViewModel.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatViewModel.kt @@ -839,7 +839,6 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { * - Поддержка Reply/Forward через attachments (как в React Native) */ fun sendMessage() { - android.util.Log.e("REPLY_DEBUG", "🚀🚀🚀 sendMessage() CALLED 🚀🚀🚀") Log.d(TAG, "🚀🚀🚀 sendMessage() CALLED 🚀🚀🚀") val text = _inputText.value.trim() @@ -849,7 +848,6 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { val replyMsgs = _replyMessages.value val isForward = _isForwardMode.value - android.util.Log.e("REPLY_DEBUG", "📝 Text: '$text', ReplyMsgs: ${replyMsgs.size}") Log.d(TAG, "📝 Text: '$text'") Log.d(TAG, "📧 Recipient: ${recipient?.take(16)}") Log.d(TAG, "👤 Sender: ${sender?.take(16)}") @@ -932,7 +930,6 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { var replyBlobPlaintext = "" // Сохраняем plaintext для БД if (replyMsgsToSend.isNotEmpty()) { - android.util.Log.e("REPLY_DEBUG", "🔥 FORMING REPLY ATTACHMENT, count=${replyMsgsToSend.size}") // Формируем JSON массив с цитируемыми сообщениями (как в RN) val replyJsonArray = JSONArray() @@ -945,17 +942,12 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { put("attachments", JSONArray()) // Пустой массив вложений } replyJsonArray.put(replyJson) - android.util.Log.e("REPLY_DEBUG", " - Reply msg: id=${msg.messageId}, text='${msg.text.take(30)}'") } replyBlobPlaintext = replyJsonArray.toString() // 🔥 Сохраняем plaintext - android.util.Log.e("REPLY_DEBUG", " - Reply blob plaintext length: ${replyBlobPlaintext.length}") - android.util.Log.e("REPLY_DEBUG", " - Reply blob plaintext: ${replyBlobPlaintext.take(100)}") // 🔥 Шифруем reply blob plainKeyAndNonce (как в React Native) val encryptedReplyBlob = MessageCrypto.encryptReplyBlob(replyBlobPlaintext, plainKeyAndNonce) - android.util.Log.e("REPLY_DEBUG", " - Encrypted reply blob length: ${encryptedReplyBlob.length}") - android.util.Log.e("REPLY_DEBUG", " - Encrypted reply blob: ${encryptedReplyBlob.take(60)}") val replyAttachmentId = "reply_${timestamp}" messageAttachments.add(MessageAttachment( @@ -964,7 +956,6 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { type = AttachmentType.MESSAGES, preview = "" )) - android.util.Log.e("REPLY_DEBUG", " ✅ Reply attachment added, id=$replyAttachmentId") } val packet = PacketMessage().apply { @@ -983,19 +974,13 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { Log.d(TAG, " - messageId: $messageId") Log.d(TAG, " - text: '$text'") Log.d(TAG, " - attachments count: ${packet.attachments.size}") - android.util.Log.e("REPLY_DEBUG", "📦 PACKET READY TO SEND:") - android.util.Log.e("REPLY_DEBUG", " - messageId: $messageId") - android.util.Log.e("REPLY_DEBUG", " - text: '$text'") - android.util.Log.e("REPLY_DEBUG", " - attachments count: ${packet.attachments.size}") packet.attachments.forEach { att -> Log.d(TAG, " - attachment: type=${att.type}, id=${att.id}, blob.length=${att.blob.length}") - android.util.Log.e("REPLY_DEBUG", " - attachment: type=${att.type}, id=${att.id}, blob.length=${att.blob.length}") } // Отправляем пакет ProtocolManager.send(packet) Log.d(TAG, "✅ PACKET SENT via ProtocolManager.send()") - android.util.Log.e("REPLY_DEBUG", "✅ PACKET SENT via ProtocolManager.send()") // 3. 🎯 UI обновление в Main потоке withContext(Dispatchers.Main) { diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListViewModel.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListViewModel.kt index f302820..7662519 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListViewModel.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListViewModel.kt @@ -55,19 +55,16 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio */ fun setAccount(publicKey: String, privateKey: String) { if (currentAccount == publicKey) { - android.util.Log.d("ChatsListViewModel", "⚠️ setAccount called again for same account, skipping") return } currentAccount = publicKey currentPrivateKey = privateKey - android.util.Log.d("ChatsListViewModel", "✅ Setting up dialogs Flow for account: ${publicKey.take(16)}...") viewModelScope.launch { dialogDao.getDialogsFlow(publicKey) .flowOn(Dispatchers.IO) // 🚀 Flow работает на IO .map { dialogsList -> - android.util.Log.d("ChatsListViewModel", "📋 Dialogs Flow emitted: ${dialogsList.size} dialogs") // 🔓 Расшифровываем lastMessage на IO потоке (PBKDF2 - тяжелая операция!) dialogsList.map { dialog -> val decryptedLastMessage = try { @@ -99,7 +96,6 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio .flowOn(Dispatchers.Default) // 🚀 map выполняется на Default (CPU) .flowOn(Dispatchers.Main) // 🎯 КРИТИЧНО: Обновляем UI на главном потоке! .collect { decryptedDialogs -> - android.util.Log.d("ChatsListViewModel", "✅ Updated UI with ${decryptedDialogs.size} decrypted dialogs") _dialogs.value = decryptedDialogs // 🟢 Подписываемся на онлайн-статусы всех собеседников @@ -156,9 +152,7 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio dialogDao.updateOpponentInfo(currentAccount, opponentKey, opponentTitle, opponentUsername, verified) } - android.util.Log.d("ChatsListViewModel", "✅ Dialog upserted from messages table") } catch (e: Exception) { - android.util.Log.e("ChatsListViewModel", "Error upserting dialog", e) } }