From b1046f88e5da2c7a0e234a22e4f397d1c0e43048 Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Fri, 16 Jan 2026 06:18:47 +0500 Subject: [PATCH] feat: Integrate AccountManager to retrieve last logged account in AuthFlow and update MainActivity --- .../com/rosetta/messenger/MainActivity.kt | 1 + .../rosetta/messenger/crypto/CryptoManager.kt | 5 - .../rosetta/messenger/crypto/MessageCrypto.kt | 24 ---- .../rosetta/messenger/data/ForwardManager.kt | 7 -- .../com/rosetta/messenger/ui/auth/AuthFlow.kt | 9 +- .../messenger/ui/chats/ChatDetailScreen.kt | 61 --------- .../messenger/ui/chats/ChatViewModel.kt | 119 +++--------------- .../messenger/ui/chats/ChatsListViewModel.kt | 5 - .../ui/components/AppleEmojiEditText.kt | 4 - .../ui/components/KeyboardHeightProvider.kt | 18 --- .../ui/components/OptimizedEmojiCache.kt | 6 - .../ui/components/OptimizedEmojiPicker.kt | 7 -- 12 files changed, 24 insertions(+), 242 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/MainActivity.kt b/app/src/main/java/com/rosetta/messenger/MainActivity.kt index 5b22aae..7fcf086 100644 --- a/app/src/main/java/com/rosetta/messenger/MainActivity.kt +++ b/app/src/main/java/com/rosetta/messenger/MainActivity.kt @@ -158,6 +158,7 @@ class MainActivity : ComponentActivity() { isDarkTheme = isDarkTheme, hasExistingAccount = screen == "auth_unlock", accounts = accountInfoList, + accountManager = accountManager, onAuthComplete = { account -> currentAccount = account hasExistingAccount = true 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 c90333f..b47f0fa 100644 --- a/app/src/main/java/com/rosetta/messenger/crypto/CryptoManager.kt +++ b/app/src/main/java/com/rosetta/messenger/crypto/CryptoManager.kt @@ -332,11 +332,6 @@ object CryptoManager { // Decompress (zlib inflate - совместимо с pako.inflate в JS) String(decompress(decrypted), Charsets.UTF_8) } catch (e: Exception) { - android.util.Log.e("ReplyDebug", "❌ [DECRYPT] decryptWithPassword failed:", e) - android.util.Log.e("ReplyDebug", " - Input length: ${encryptedData.length}") - android.util.Log.e("ReplyDebug", " - Input preview: ${encryptedData.take(100)}") - android.util.Log.e("ReplyDebug", " - Password length: ${password.length}") - android.util.Log.e("ReplyDebug", " - Exception: ${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 71d1d8f..28858d0 100644 --- a/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt +++ b/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt @@ -606,15 +606,10 @@ object MessageCrypto { */ fun encryptReplyBlob(replyJson: String, plainKeyAndNonce: ByteArray): String { return try { - android.util.Log.d("ReplyDebug", "🔐 encryptReplyBlob called:") - android.util.Log.d("ReplyDebug", " - plainKeyAndNonce size: ${plainKeyAndNonce.size}") - android.util.Log.d("ReplyDebug", " - plainKeyAndNonce hex: ${plainKeyAndNonce.joinToString("") { "%02x".format(it) }}") // Convert plainKeyAndNonce 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("ReplyDebug", " - password length: ${password.length}") - android.util.Log.d("ReplyDebug", " - password bytes (first 20): ${password.toByteArray(Charsets.UTF_8).take(20).joinToString(",")}") // Compress with pako (deflate) val deflater = java.util.zip.Deflater() @@ -817,37 +812,25 @@ object MessageCrypto { */ fun decryptReplyBlob(encryptedBlob: String, plainKeyAndNonce: ByteArray): String { return try { - android.util.Log.d("ReplyDebug", "🔓 decryptReplyBlob called:") - android.util.Log.d("ReplyDebug", " - Input length: ${encryptedBlob.length}") - android.util.Log.d("ReplyDebug", " - plainKeyAndNonce size: ${plainKeyAndNonce.size}") - android.util.Log.d("ReplyDebug", " - plainKeyAndNonce hex: ${plainKeyAndNonce.joinToString("") { "%02x".format(it) }}") // Check if it's encrypted format (contains ':') if (!encryptedBlob.contains(':')) { - android.util.Log.d("ReplyDebug", " - No ':' found, returning as-is") return encryptedBlob } // Parse ivBase64:ciphertextBase64 val parts = encryptedBlob.split(':') if (parts.size != 2) { - android.util.Log.d("ReplyDebug", " - Invalid format (not 2 parts), returning as-is") return encryptedBlob } - android.util.Log.d("ReplyDebug", " - IV part length: ${parts[0].length}") - android.util.Log.d("ReplyDebug", " - Ciphertext part length: ${parts[1].length}") val iv = Base64.decode(parts[0], Base64.DEFAULT) val ciphertext = Base64.decode(parts[1], Base64.DEFAULT) - android.util.Log.d("ReplyDebug", " - Decoded IV size: ${iv.size}") - android.util.Log.d("ReplyDebug", " - Decoded ciphertext size: ${ciphertext.size}") // Password from plainKeyAndNonce - use same JS-like UTF-8 conversion val password = bytesToJsUtf8String(plainKeyAndNonce) - android.util.Log.d("ReplyDebug", " - Password length: ${password.length}") - android.util.Log.d("ReplyDebug", " - Password bytes hex: ${password.toByteArray(Charsets.UTF_8).joinToString("") { "%02x".format(it) }}") // PBKDF2 key derivation // CRITICAL: Must use SHA256 to match React Native (not SHA1!) @@ -861,7 +844,6 @@ object MessageCrypto { val secretKey = factory.generateSecret(spec) val keyBytes = secretKey.encoded - android.util.Log.d("ReplyDebug", " - Derived key size: ${keyBytes.size}") // AES-CBC decryption val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") @@ -870,7 +852,6 @@ object MessageCrypto { cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec) val decompressed = cipher.doFinal(ciphertext) - android.util.Log.d("ReplyDebug", " - Decrypted (compressed) size: ${decompressed.size}") // Decompress with inflate val inflater = java.util.zip.Inflater() @@ -880,14 +861,9 @@ object MessageCrypto { inflater.end() val plaintext = String(outputBuffer, 0, outputSize, Charsets.UTF_8) - android.util.Log.d("ReplyDebug", " - Decompressed plaintext length: ${plaintext.length}") - android.util.Log.d("ReplyDebug", " - Plaintext preview: ${plaintext.take(100)}") - android.util.Log.d("ReplyDebug", "✅ decryptReplyBlob success") plaintext } catch (e: Exception) { - android.util.Log.e("ReplyDebug", "❌ decryptReplyBlob failed:", e) - android.util.Log.e("ReplyDebug", " - Exception: ${e.javaClass.simpleName}: ${e.message}") // Return as-is, might be plain JSON encryptedBlob } diff --git a/app/src/main/java/com/rosetta/messenger/data/ForwardManager.kt b/app/src/main/java/com/rosetta/messenger/data/ForwardManager.kt index d7b15f0..1fab095 100644 --- a/app/src/main/java/com/rosetta/messenger/data/ForwardManager.kt +++ b/app/src/main/java/com/rosetta/messenger/data/ForwardManager.kt @@ -49,7 +49,6 @@ object ForwardManager { messages: List, showPicker: Boolean = true ) { - android.util.Log.d("ForwardManager", "📨 Setting forward messages: ${messages.size}") _forwardMessages.value = messages if (showPicker) { _showChatPicker.value = true @@ -60,7 +59,6 @@ object ForwardManager { * Выбрать чат для пересылки */ fun selectChat(publicKey: String) { - android.util.Log.d("ForwardManager", "📨 Selected chat: $publicKey") _selectedChatPublicKey.value = publicKey _showChatPicker.value = false } @@ -69,7 +67,6 @@ object ForwardManager { * Скрыть выбор чата (отмена) */ fun hideChatPicker() { - android.util.Log.d("ForwardManager", "📨 Hide chat picker") _showChatPicker.value = false } @@ -79,7 +76,6 @@ object ForwardManager { */ fun consumeForwardMessages(): List { val messages = _forwardMessages.value - android.util.Log.d("ForwardManager", "📨 Consuming forward messages: ${messages.size}") return messages } @@ -87,7 +83,6 @@ object ForwardManager { * Очистить все данные (после применения или отмены) */ fun clear() { - android.util.Log.d("ForwardManager", "📨 Clearing forward state") _forwardMessages.value = emptyList() _showChatPicker.value = false _selectedChatPublicKey.value = null @@ -104,7 +99,6 @@ object ForwardManager { fun hasForwardMessagesForChat(publicKey: String): Boolean { val selectedKey = _selectedChatPublicKey.value val hasMessages = _forwardMessages.value.isNotEmpty() - android.util.Log.d("ForwardManager", "📨 hasForwardMessagesForChat($publicKey): selectedKey=$selectedKey, hasMessages=$hasMessages") return selectedKey == publicKey && hasMessages } @@ -115,7 +109,6 @@ object ForwardManager { fun getForwardMessagesForChat(publicKey: String): List { val selectedKey = _selectedChatPublicKey.value return if (selectedKey == publicKey && _forwardMessages.value.isNotEmpty()) { - android.util.Log.d("ForwardManager", "📨 getForwardMessagesForChat: returning ${_forwardMessages.value.size} messages") _forwardMessages.value } else { emptyList() diff --git a/app/src/main/java/com/rosetta/messenger/ui/auth/AuthFlow.kt b/app/src/main/java/com/rosetta/messenger/ui/auth/AuthFlow.kt index a1964c8..fed2710 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/auth/AuthFlow.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/auth/AuthFlow.kt @@ -5,6 +5,7 @@ import androidx.compose.animation.* import androidx.compose.animation.core.tween import androidx.compose.runtime.* import com.rosetta.messenger.data.DecryptedAccount +import com.rosetta.messenger.data.AccountManager enum class AuthScreen { SELECT_ACCOUNT, @@ -21,6 +22,7 @@ fun AuthFlow( isDarkTheme: Boolean, hasExistingAccount: Boolean, accounts: List = emptyList(), + accountManager: AccountManager, onAuthComplete: (DecryptedAccount?) -> Unit, onLogout: () -> Unit = {} ) { @@ -33,7 +35,12 @@ fun AuthFlow( ) } var seedPhrase by remember { mutableStateOf>(emptyList()) } - var selectedAccountId by remember { mutableStateOf(accounts.firstOrNull()?.id) } + // Use last logged account or fallback to first account + var selectedAccountId by remember { + mutableStateOf( + accountManager.getLastLoggedPublicKey() ?: accounts.firstOrNull()?.id + ) + } var showCreateModal by remember { mutableStateOf(false) } // Handle system back button 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 9d5efbd..771c045 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 @@ -333,18 +333,12 @@ fun ChatDetailScreen( // Логирование изменений selection mode LaunchedEffect(isSelectionMode, selectedMessages.size) { - android.util.Log.d("ChatDetailScreen", "═══════════════════════════════════════") - android.util.Log.d("ChatDetailScreen", "📝 SELECTION MODE CHANGED") - android.util.Log.d("ChatDetailScreen", " 📊 isSelectionMode: $isSelectionMode") - android.util.Log.d("ChatDetailScreen", " 📊 selectedMessages.size: ${selectedMessages.size}") - android.util.Log.d("ChatDetailScreen", "═══════════════════════════════════════") } // 🔥 Backup: если клавиатура ещё открыта когда selection mode активировался // (клавиатура уже должна быть закрыта в onLongClick, это только backup) LaunchedEffect(isSelectionMode) { if (isSelectionMode) { - android.util.Log.d("ChatDetailScreen", "⚠️ Backup keyboard hide triggered") // Backup закрытие клавиатуры (основное в onLongClick) keyboardController?.hide() } @@ -445,13 +439,9 @@ fun ChatDetailScreen( // 🔥 Функция для скролла к сообщению с подсветкой val scrollToMessage: (String) -> Unit = { messageId -> - android.util.Log.d("ChatDetail", "🔍 scrollToMessage called for: '$messageId'") - android.util.Log.d("ChatDetail", " - messageId length: ${messageId.length}") - android.util.Log.d("ChatDetail", " - Total messages: ${messagesWithDates.size}") // Логируем все ID сообщений для отладки messagesWithDates.forEachIndexed { index, pair -> - android.util.Log.d("ChatDetail", " - [$index] id='${pair.first.id}', text='${pair.first.text.take(20)}...'") } scope.launch { @@ -461,18 +451,15 @@ fun ChatDetailScreen( // Находим индекс сообщения в списке val messageIndex = messagesWithDates.indexOfFirst { it.first.id == messageId } - android.util.Log.d("ChatDetail", " - Found at index: $messageIndex") if (messageIndex != -1) { // Скроллим к сообщению listState.animateScrollToItem(messageIndex) - android.util.Log.d("ChatDetail", " ✅ Scrolled to message") // Подсвечиваем на 2 секунды highlightedMessageId = messageId delay(2000) highlightedMessageId = null } else { - android.util.Log.d("ChatDetail", " ❌ Message not found in list") } } } @@ -1018,14 +1005,6 @@ fun ChatDetailScreen( // Логирование состояния LaunchedEffect(isSelectionMode, useImePadding, coordinator.isEmojiBoxVisible, coordinator.keyboardHeight) { - android.util.Log.d("ChatDetailScreen", "═══════════════════════════════════════") - android.util.Log.d("ChatDetailScreen", "🔄 BOTTOM BAR STATE CHANGED") - android.util.Log.d("ChatDetailScreen", " 📊 isSelectionMode: $isSelectionMode") - android.util.Log.d("ChatDetailScreen", " 📊 useImePadding: $useImePadding") - android.util.Log.d("ChatDetailScreen", " 📊 isEmojiBoxVisible: ${coordinator.isEmojiBoxVisible}") - android.util.Log.d("ChatDetailScreen", " 📊 keyboardHeight: ${coordinator.keyboardHeight}") - android.util.Log.d("ChatDetailScreen", " 📊 emojiHeight: ${coordinator.emojiHeight}") - android.util.Log.d("ChatDetailScreen", "═══════════════════════════════════════") } Column(modifier = bottomModifier) { @@ -1057,7 +1036,6 @@ fun ChatDetailScreen( }, label = "bottomBarContent" ) { selectionMode -> - android.util.Log.d("ChatDetailScreen", "🎬 AnimatedContent to selectionMode=$selectionMode") if (selectionMode) { // SELECTION ACTION BAR - Reply/Forward @@ -1360,19 +1338,13 @@ fun ChatDetailScreen( isSelected = selectedMessages.contains(selectionKey), isHighlighted = highlightedMessageId == message.id, onLongClick = { - android.util.Log.d("ChatDetailScreen", "═══════════════════════════════════════") - android.util.Log.d("ChatDetailScreen", "👆 LONG CLICK on message") - android.util.Log.d("ChatDetailScreen", " 📊 isSelectionMode BEFORE: $isSelectionMode") - android.util.Log.d("ChatDetailScreen", " 📊 selectedMessages.size BEFORE: ${selectedMessages.size}") // 🔥 СНАЧАЛА закрываем клавиатуру МГНОВЕННО (до изменения state) if (!isSelectionMode) { - android.util.Log.d("ChatDetailScreen", " ⌨️ Closing keyboard...") val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.hideSoftInputFromWindow(view.windowToken, 0) focusManager.clearFocus() showEmojiPicker = false - android.util.Log.d("ChatDetailScreen", " ✅ Keyboard closed") } // Toggle selection on long press selectedMessages = if (selectedMessages.contains(selectionKey)) { @@ -1381,8 +1353,6 @@ fun ChatDetailScreen( selectedMessages + selectionKey } - android.util.Log.d("ChatDetailScreen", " 📊 selectedMessages.size AFTER: ${selectedMessages.size}") - android.util.Log.d("ChatDetailScreen", "═══════════════════════════════════════") }, onClick = { // If in selection mode, toggle selection @@ -1905,7 +1875,6 @@ private fun MessageBubble( isOutgoing = message.isOutgoing, isDarkTheme = isDarkTheme, onClick = { - android.util.Log.d("ChatDetail", "🖱️ Reply clicked: ${reply.messageId}") onReplyClick(reply.messageId) } ) @@ -2238,22 +2207,16 @@ private fun MessageInputBar( // 🔥 Автофокус при открытии reply панели LaunchedEffect(hasReply, editTextView) { if (hasReply) { - android.util.Log.d("EmojiPicker", "═══════════════════════════════════════════════════════") - android.util.Log.d("EmojiPicker", "💬 Reply panel opened, hasReply=$hasReply") - android.util.Log.d("EmojiPicker", " 📊 editTextView=$editTextView, showEmojiPicker=$showEmojiPicker") // Даём время на создание view если ещё null kotlinx.coroutines.delay(50) editTextView?.let { editText -> // 🔥 НЕ открываем клавиатуру если emoji уже открыт if (!showEmojiPicker) { - android.util.Log.d("EmojiPicker", " ⌨️ Requesting focus and keyboard for reply...") editText.requestFocus() // Открываем клавиатуру val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT) - android.util.Log.d("EmojiPicker", " ✅ Auto-opened keyboard for reply") } else { - android.util.Log.d("EmojiPicker", " ⏭️ Skip auto-keyboard for reply (emoji is open)") } } } @@ -2356,7 +2319,6 @@ private fun MessageInputBar( val timeSinceLastToggle = currentTime - lastToggleTime if (timeSinceLastToggle < toggleCooldownMs) { - android.util.Log.d("EmojiPicker", "⏸️ Toggle blocked: ${timeSinceLastToggle}ms < ${toggleCooldownMs}ms") return } @@ -2364,45 +2326,34 @@ private fun MessageInputBar( val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - android.util.Log.d("EmojiPicker", "=".repeat(60)) - android.util.Log.d("EmojiPicker", "🔥 toggleEmojiPicker START") - android.util.Log.d("EmojiPicker", " showEmojiPicker(local)=$showEmojiPicker") coordinator.logState() // 🔥 ИСПОЛЬЗУЕМ coordinator.isEmojiVisible вместо showEmojiPicker для более точного состояния if (coordinator.isEmojiVisible) { // ========== EMOJI → KEYBOARD ========== - android.util.Log.d("EmojiPicker", "🔄 Switching: Emoji → Keyboard") coordinator.requestShowKeyboard( showKeyboard = { editTextView?.let { editText -> editText.requestFocus() imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED) - android.util.Log.d("EmojiPicker", "📱 Keyboard show requested") } }, hideEmoji = { onToggleEmojiPicker(false) - android.util.Log.d("EmojiPicker", "😊 Emoji panel hidden") } ) } else { // ========== KEYBOARD → EMOJI ========== - android.util.Log.d("EmojiPicker", "🔄 Switching: Keyboard → Emoji") coordinator.requestShowEmoji( hideKeyboard = { imm.hideSoftInputFromWindow(view.windowToken, 0) - android.util.Log.d("EmojiPicker", "⌨️ Keyboard hide requested") }, showEmoji = { onToggleEmojiPicker(true) - android.util.Log.d("EmojiPicker", "😊 Emoji panel shown") } ) } - android.util.Log.d("EmojiPicker", "🔥 toggleEmojiPicker END") - android.util.Log.d("EmojiPicker", "=".repeat(60)) } // Функция отправки - НЕ закрывает клавиатуру (UX правило #6) @@ -2643,24 +2594,12 @@ private fun MessageInputBar( editTextView = view }, onFocusChanged = { hasFocus -> - android.util.Log.d("EmojiPicker", "═══════════════════════════════════════════════════════") - android.util.Log.d("EmojiPicker", "🎯 TextField focus changed: hasFocus=$hasFocus") - android.util.Log.d("EmojiPicker", " 📊 Current state:") - android.util.Log.d("EmojiPicker", " - showEmojiPicker=$showEmojiPicker") - android.util.Log.d("EmojiPicker", " - coordinator.isEmojiVisible=${coordinator.isEmojiVisible}") - android.util.Log.d("EmojiPicker", " - coordinator.isKeyboardVisible=${coordinator.isKeyboardVisible}") - android.util.Log.d("EmojiPicker", " - coordinator.currentState=${coordinator.currentState}") // Если TextField получил фокус И emoji открыт → закрываем emoji if (hasFocus && showEmojiPicker) { - android.util.Log.d("EmojiPicker", "🔄 TextField focused while emoji open → closing emoji") - android.util.Log.d("EmojiPicker", " 📞 Calling onToggleEmojiPicker(false)...") onToggleEmojiPicker(false) - android.util.Log.d("EmojiPicker", " ✅ Emoji close requested") } else if (hasFocus && !showEmojiPicker) { - android.util.Log.d("EmojiPicker", "⌨️ TextField focused with emoji closed → normal keyboard behavior") } else if (!hasFocus) { - android.util.Log.d("EmojiPicker", "👋 TextField lost focus") } } ) 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 ac362ee..565ff7c 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 @@ -229,36 +229,24 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { var blobToStore = att.blob // По умолчанию сохраняем оригинальный blob if (att.type == AttachmentType.MESSAGES && att.blob.isNotEmpty()) { try { - android.util.Log.d("ReplyDebug", "📥 [RECEIVE] Processing reply attachment:") - android.util.Log.d("ReplyDebug", " - Encrypted blob length: ${att.blob.length}") - android.util.Log.d("ReplyDebug", " - Encrypted blob preview: ${att.blob.take(100)}") - android.util.Log.d("ReplyDebug", " - Decrypting with plainKeyAndNonce (${plainKeyAndNonce.size} bytes)") - android.util.Log.d("ReplyDebug", " - plainKeyAndNonce hex: ${plainKeyAndNonce.joinToString("") { "%02x".format(it) }}") // 🔥 Расшифровываем с полным plainKeyAndNonce (56 bytes) // Desktop использует chachaDecryptedKey.toString('utf-8') = полные 56 байт! val decryptedBlob = MessageCrypto.decryptReplyBlob(att.blob, plainKeyAndNonce) - android.util.Log.d("ReplyDebug", " - Decrypted blob length: ${decryptedBlob.length}") - android.util.Log.d("ReplyDebug", " - Decrypted blob preview: ${decryptedBlob.take(200)}") // 🔥 Сохраняем расшифрованный blob в БД blobToStore = decryptedBlob // Парсим JSON массив с цитируемыми сообщениями val replyArray = JSONArray(decryptedBlob) - android.util.Log.d("ReplyDebug", " - Reply array length: ${replyArray.length()}") if (replyArray.length() > 0) { val firstReply = replyArray.getJSONObject(0) val replyPublicKey = firstReply.optString("publicKey", "") val replyText = firstReply.optString("message", "") val replyMessageId = firstReply.optString("message_id", "") - android.util.Log.d("ReplyDebug", " - Parsed reply: id=$replyMessageId") - android.util.Log.d("ReplyDebug", " publicKey=${replyPublicKey.take(20)}...") - android.util.Log.d("ReplyDebug", " message=${replyText.take(50)}") // Определяем автора цитаты val isReplyFromMe = replyPublicKey == myPublicKey - android.util.Log.d("ReplyDebug", " - Is reply from me: $isReplyFromMe") replyData = ReplyData( messageId = replyMessageId, @@ -266,11 +254,8 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { text = replyText, isFromMe = isReplyFromMe ) - android.util.Log.d("ReplyDebug", "✅ [RECEIVE] Reply data created successfully") } } catch (e: Exception) { - android.util.Log.e("ReplyDebug", "❌ [RECEIVE] Failed to decrypt/parse reply:", e) - android.util.Log.e("ReplyDebug", " - Encrypted blob: ${att.blob.take(100)}") } } @@ -364,7 +349,6 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { val forwardMessages = ForwardManager.getForwardMessagesForChat(publicKey) val hasForward = forwardMessages.isNotEmpty() if (hasForward) { - android.util.Log.d("ChatViewModel", "📨 Will apply ${forwardMessages.size} forward messages") } // Сбрасываем состояние @@ -380,7 +364,6 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { // 📨 Применяем Forward сообщения СРАЗУ после сброса if (hasForward) { - android.util.Log.d("ChatViewModel", "📨 Applying ${forwardMessages.size} forward messages NOW") // Конвертируем ForwardMessage в ReplyMessage _replyMessages.value = forwardMessages.map { fm -> ReplyMessage( @@ -392,7 +375,6 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { ) } _isForwardMode.value = true - android.util.Log.d("ChatViewModel", "📨 isForwardMode set to TRUE, replyMessages.size = ${_replyMessages.value.size}") // Очищаем ForwardManager после применения ForwardManager.clear() } else { @@ -446,7 +428,21 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { return@launch } - // 🔥 Нет кэша - показываем скелетон и загружаем с задержкой для анимации + // 🔥 Нет кэша - проверяем есть ли вообще сообщения в БД + // Если диалог пустой - не показываем скелетон! + val totalCount = messageDao.getMessageCount(account, dialogKey) + + if (totalCount == 0) { + // Пустой диалог - сразу показываем пустое состояние без скелетона + withContext(Dispatchers.Main.immediate) { + _messages.value = emptyList() + _isLoading.value = false + } + isLoadingMessages = false + return@launch + } + + // 🔥 Есть сообщения - показываем скелетон и загружаем с задержкой для анимации if (delayMs > 0) { withContext(Dispatchers.Main.immediate) { _isLoading.value = true // Показываем скелетон @@ -455,9 +451,6 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { } - // 🔍 Проверяем общее количество сообщений в диалоге - val totalCount = messageDao.getMessageCount(account, dialogKey) - // 🔥 Получаем первую страницу - БЕЗ suspend задержки val entities = messageDao.getMessages(account, dialogKey, limit = PAGE_SIZE, offset = 0) @@ -635,10 +628,6 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { * Как в архиве: расшифровываем при каждой загрузке */ private suspend fun entityToChatMessage(entity: MessageEntity): ChatMessage { - android.util.Log.d("ReplyDebug", "🔄 [DB LOAD] entityToChatMessage called") - android.util.Log.d("ReplyDebug", " - messageId: ${entity.messageId}") - android.util.Log.d("ReplyDebug", " - attachments: ${entity.attachments}") - android.util.Log.d("ReplyDebug", " - attachments length: ${entity.attachments.length}") // Расшифровываем сообщение из content + chachaKey var displayText = try { @@ -677,35 +666,27 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { } } - android.util.Log.d("ReplyDebug", " - Decrypted text: ${displayText.take(50)}") // Парсим attachments для поиска MESSAGES (цитата) // 🔥 ВАЖНО: Передаем content и chachaKey для расшифровки reply blob если нужно - android.util.Log.d("ReplyDebug", " - Calling parseReplyFromAttachments...") var replyData = parseReplyFromAttachments( attachmentsJson = entity.attachments, isFromMe = entity.fromMe == 1, content = entity.content, chachaKey = entity.chachaKey ) - android.util.Log.d("ReplyDebug", " - parseReplyFromAttachments returned: ${if (replyData != null) "NOT NULL" else "NULL"}") // Если не нашли reply в attachments, пробуем распарсить из текста if (replyData == null) { - android.util.Log.d("ReplyDebug", " - Reply is null, trying to parse from text...") val parseResult = parseReplyFromText(displayText) if (parseResult != null) { - android.util.Log.d("ReplyDebug", " - ✅ Parsed reply from text") replyData = parseResult.first displayText = parseResult.second } else { - android.util.Log.d("ReplyDebug", " - ❌ Failed to parse reply from text") } } else { - android.util.Log.d("ReplyDebug", " - ✅ Reply found in attachments!") } - android.util.Log.d("ReplyDebug", " - Creating ChatMessage with replyData: ${if (replyData != null) "NOT NULL" else "NULL"}") return ChatMessage( id = entity.messageId, @@ -758,139 +739,98 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { content: String, chachaKey: String ): ReplyData? { - android.util.Log.d("ReplyDebug", "🔍 [DB LOAD] parseReplyFromAttachments called") - android.util.Log.d("ReplyDebug", " - attachmentsJson.isEmpty(): ${attachmentsJson.isEmpty()}") - android.util.Log.d("ReplyDebug", " - attachmentsJson == '[]': ${attachmentsJson == "[]"}") if (attachmentsJson.isEmpty() || attachmentsJson == "[]") { - android.util.Log.d("ReplyDebug", " - Early return: attachments empty or []") return null } return try { - android.util.Log.d("ReplyDebug", "💾 [DB LOAD] Parsing reply from attachments JSON") - android.util.Log.d("ReplyDebug", " - Full JSON: $attachmentsJson") - android.util.Log.d("ReplyDebug", " - JSON length: ${attachmentsJson.length}") val attachments = JSONArray(attachmentsJson) - android.util.Log.d("ReplyDebug", " - Attachments count: ${attachments.length()}") for (i in 0 until attachments.length()) { - android.util.Log.d("ReplyDebug", " - Processing attachment $i...") val attachment = attachments.getJSONObject(i) val type = attachment.optInt("type", 0) - android.util.Log.d("ReplyDebug", " - type: $type") // MESSAGES = 1 (цитата) if (type == 1) { - android.util.Log.d("ReplyDebug", " - ✅ Found MESSAGES type!") // Данные могут быть в blob или preview var dataJson = attachment.optString("blob", "") - android.util.Log.d("ReplyDebug", " - blob from JSON: ${if (dataJson.isEmpty()) "EMPTY" else "length=${dataJson.length}"}") if (dataJson.isEmpty()) { dataJson = attachment.optString("preview", "") - android.util.Log.d("ReplyDebug", " - blob was empty, trying preview: ${if (dataJson.isEmpty()) "ALSO EMPTY" else "length=${dataJson.length}"}") } - android.util.Log.d("ReplyDebug", " - Final data length: ${dataJson.length}") - android.util.Log.d("ReplyDebug", " - Data preview (first 200): ${dataJson.take(200)}") if (dataJson.isEmpty()) { - android.util.Log.e("ReplyDebug", " - ❌ Data is empty, skipping") continue } // 🔥 Проверяем формат blob - если содержит ":", то это зашифрованный формат "iv:ciphertext" val colonCount = dataJson.count { it == ':' } - android.util.Log.d("ReplyDebug", " - Colon count in data: $colonCount") if (dataJson.contains(":") && dataJson.split(":").size == 2) { - android.util.Log.d("ReplyDebug", " - 🔒 Blob is ENCRYPTED (iv:ciphertext format)") val privateKey = myPrivateKey var decryptionSuccess = false // 🔥 Способ 1: Пробуем расшифровать с приватным ключом (для исходящих сообщений) if (privateKey != null) { - android.util.Log.d("ReplyDebug", " - Attempting to decrypt with private key...") try { val decrypted = CryptoManager.decryptWithPassword(dataJson, privateKey) if (decrypted != null) { - android.util.Log.d("ReplyDebug", " - ✅ Decrypted with private key") - android.util.Log.d("ReplyDebug", " - Decrypted preview: ${decrypted.take(200)}") dataJson = decrypted decryptionSuccess = true } } catch (e: Exception) { - android.util.Log.d("ReplyDebug", " - Private key decryption failed: ${e.message}") } } // 🔥 Способ 2: Пробуем расшифровать с ChaCha ключом сообщения (для входящих) if (!decryptionSuccess && content.isNotEmpty() && chachaKey.isNotEmpty() && privateKey != null) { - android.util.Log.d("ReplyDebug", " - Attempting to decrypt with ChaCha key...") try { val decrypted = MessageCrypto.decryptAttachmentBlob(dataJson, chachaKey, privateKey) if (decrypted != null) { - android.util.Log.d("ReplyDebug", " - ✅ Decrypted with ChaCha key") - android.util.Log.d("ReplyDebug", " - Decrypted preview: ${decrypted.take(200)}") dataJson = decrypted decryptionSuccess = true } } catch (e: Exception) { - android.util.Log.d("ReplyDebug", " - ChaCha decryption failed: ${e.message}") } } // 🔥 Способ 3: Пробуем decryptReplyBlob с plainKeyAndNonce if (!decryptionSuccess && content.isNotEmpty() && chachaKey.isNotEmpty() && privateKey != null) { - android.util.Log.d("ReplyDebug", " - Attempting to decrypt with plainKeyAndNonce...") try { val decryptResult = MessageCrypto.decryptIncomingFull(content, chachaKey, privateKey) val plainKeyAndNonce = decryptResult.plainKeyAndNonce val decrypted = MessageCrypto.decryptReplyBlob(dataJson, plainKeyAndNonce) if (decrypted.isNotEmpty()) { - android.util.Log.d("ReplyDebug", " - ✅ Decrypted with plainKeyAndNonce") - android.util.Log.d("ReplyDebug", " - Decrypted preview: ${decrypted.take(200)}") dataJson = decrypted decryptionSuccess = true } } catch (e: Exception) { - android.util.Log.d("ReplyDebug", " - plainKeyAndNonce decryption failed: ${e.message}") } } if (!decryptionSuccess) { - android.util.Log.e("ReplyDebug", " - ❌ All decryption methods failed") continue } } else { - android.util.Log.d("ReplyDebug", " - 📄 Blob is PLAINTEXT (not encrypted)") } - android.util.Log.d("ReplyDebug", " - Attempting to parse as JSONArray...") val messagesArray = try { JSONArray(dataJson) } catch (e: Exception) { - android.util.Log.e("ReplyDebug", " - ❌ Failed to parse as JSONArray: ${e.message}") - android.util.Log.e("ReplyDebug", " - Data was: $dataJson") continue } - android.util.Log.d("ReplyDebug", " - Messages array length: ${messagesArray.length()}") if (messagesArray.length() > 0) { - android.util.Log.d("ReplyDebug", " - Extracting first message from array...") val replyMessage = messagesArray.getJSONObject(0) val replyPublicKey = replyMessage.optString("publicKey", "") val replyText = replyMessage.optString("message", "") val replyMessageIdFromJson = replyMessage.optString("message_id", "") val replyTimestamp = replyMessage.optLong("timestamp", 0L) - android.util.Log.d("ReplyDebug", " - replyMessageId from JSON: $replyMessageIdFromJson") - android.util.Log.d("ReplyDebug", " - replyPublicKey: ${replyPublicKey.take(20)}...") - android.util.Log.d("ReplyDebug", " - replyText: ${replyText.take(50)}") - android.util.Log.d("ReplyDebug", " - replyTimestamp: $replyTimestamp") // 🔥 ВАЖНО: message_id из JSON может не совпадать с messageId в Android БД! // Пытаемся найти реальный messageId в текущих сообщениях по тексту и timestamp @@ -906,15 +846,12 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { timestampTo = replyTimestamp + 5000 )?.messageId ?: replyMessageIdFromJson } catch (e: Exception) { - android.util.Log.w("ReplyDebug", " - ⚠️ Could not find real messageId, using JSON id") replyMessageIdFromJson } - android.util.Log.d("ReplyDebug", " - Real messageId: $realMessageId") // Определяем, кто автор цитируемого сообщения val isReplyFromMe = replyPublicKey == myPublicKey - android.util.Log.d("ReplyDebug", " - isReplyFromMe: $isReplyFromMe") val result = ReplyData( messageId = realMessageId, @@ -922,20 +859,14 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { text = replyText, isFromMe = isReplyFromMe ) - android.util.Log.d("ReplyDebug", " - ✅ Created ReplyData: senderName=${result.senderName}, messageId=${result.messageId}") - android.util.Log.d("ReplyDebug", "✅ [DB LOAD] Reply data parsed successfully from DB - RETURNING") return result } else { - android.util.Log.e("ReplyDebug", " - ❌ Messages array is empty") } } else { - android.util.Log.d("ReplyDebug", " - ⏭️ Skipping attachment: type != 1") } } - android.util.Log.d("ReplyDebug", "💾 [DB LOAD] No MESSAGES attachment found") null } catch (e: Exception) { - android.util.Log.e("ReplyDebug", "❌ [DB LOAD] Failed to parse reply from attachments:", e) null } } @@ -1124,13 +1055,10 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { var replyBlobForDatabase = "" // Зашифрованный blob для БД (приватным ключом) if (replyMsgsToSend.isNotEmpty()) { - android.util.Log.d("ReplyDebug", "📤 [SEND] Creating reply attachment:") - android.util.Log.d("ReplyDebug", " - Reply messages count: ${replyMsgsToSend.size}") // Формируем JSON массив с цитируемыми сообщениями (как в RN) val replyJsonArray = JSONArray() replyMsgsToSend.forEach { msg -> - android.util.Log.d("ReplyDebug", " - Adding reply: id=${msg.messageId}, publicKey=${msg.publicKey.take(20)}..., text=${msg.text.take(30)}") val replyJson = JSONObject().apply { put("message_id", msg.messageId) put("publicKey", msg.publicKey) @@ -1142,18 +1070,12 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { } val replyBlobPlaintext = replyJsonArray.toString() - android.util.Log.d("ReplyDebug", " - Reply blob plaintext length: ${replyBlobPlaintext.length}") - android.util.Log.d("ReplyDebug", " - Reply blob preview: ${replyBlobPlaintext.take(100)}") // 🔥 Шифруем reply blob (для network transmission) с ChaCha ключом - android.util.Log.d("ReplyDebug", " - Encrypting reply blob with plainKeyAndNonce (${plainKeyAndNonce.size} bytes)") val encryptedReplyBlob = MessageCrypto.encryptReplyBlob(replyBlobPlaintext, plainKeyAndNonce) - android.util.Log.d("ReplyDebug", " - Encrypted blob length: ${encryptedReplyBlob.length}") - android.util.Log.d("ReplyDebug", " - Encrypted blob preview: ${encryptedReplyBlob.take(100)}") // 🔥 Re-encrypt с приватным ключом для хранения в БД (как в Desktop Архиве) replyBlobForDatabase = CryptoManager.encryptWithPassword(replyBlobPlaintext, privateKey) - android.util.Log.d("ReplyDebug", " - Re-encrypted for DB length: ${replyBlobForDatabase.length}") val replyAttachmentId = "reply_${timestamp}" messageAttachments.add(MessageAttachment( @@ -1162,7 +1084,6 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { type = AttachmentType.MESSAGES, preview = "" )) - android.util.Log.d("ReplyDebug", "✅ [SEND] Reply attachment added to packet (encrypted)") } val packet = PacketMessage().apply { @@ -1177,17 +1098,7 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { } // 🔥 DEBUG: Log packet before sending - android.util.Log.d("ReplyDebug", "📤 [SEND] About to send packet:") - android.util.Log.d("ReplyDebug", " - messageId: $messageId") - android.util.Log.d("ReplyDebug", " - from: ${sender.take(20)}...") - android.util.Log.d("ReplyDebug", " - to: ${recipient.take(20)}...") - android.util.Log.d("ReplyDebug", " - attachments count: ${packet.attachments.size}") packet.attachments.forEachIndexed { idx, att -> - android.util.Log.d("ReplyDebug", " - Attachment $idx:") - android.util.Log.d("ReplyDebug", " type: ${att.type.value}") - android.util.Log.d("ReplyDebug", " id: ${att.id}") - android.util.Log.d("ReplyDebug", " blob length: ${att.blob.length}") - android.util.Log.d("ReplyDebug", " blob preview: ${att.blob.take(100)}") } // Отправляем пакет 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 50575c8..73058a0 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 @@ -186,7 +186,6 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio "$opponentKey:$currentAccount" } - android.util.Log.d("ChatsListViewModel", "Deleting dialog with key: $dialogKey") // Удаляем все сообщения из диалога по dialog_key database.messageDao().deleteDialog( @@ -205,9 +204,7 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio opponentKey = opponentKey ) - android.util.Log.d("ChatsListViewModel", "Dialog deleted successfully: $opponentKey") } catch (e: Exception) { - android.util.Log.e("ChatsListViewModel", "Error deleting dialog", e) // В случае ошибки - возвращаем диалог обратно (откатываем оптимистичное обновление) // Flow обновится автоматически из БД } @@ -227,7 +224,6 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio ) ) } catch (e: Exception) { - android.util.Log.e("ChatsListViewModel", "Error blocking user", e) } } @@ -240,7 +236,6 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio try { database.blacklistDao().unblockUser(publicKey, currentAccount) } catch (e: Exception) { - android.util.Log.e("ChatsListViewModel", "Error unblocking user", e) } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/components/AppleEmojiEditText.kt b/app/src/main/java/com/rosetta/messenger/ui/components/AppleEmojiEditText.kt index 2740bc3..9679c0d 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/components/AppleEmojiEditText.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/components/AppleEmojiEditText.kt @@ -269,11 +269,7 @@ fun AppleEmojiTextField( editTextView = this // Подключаем callback для изменения фокуса setOnFocusChangeListener { _, hasFocus -> - android.util.Log.d("AppleEmojiTextField", "═══════════════════════════════════════════════════════") - android.util.Log.d("AppleEmojiTextField", "🎯 Native EditText focus changed: hasFocus=$hasFocus") - android.util.Log.d("AppleEmojiTextField", " 📍 Calling onFocusChanged callback...") onFocusChanged?.invoke(hasFocus) - android.util.Log.d("AppleEmojiTextField", " ✅ onFocusChanged callback completed") } // Уведомляем о создании view onViewCreated?.invoke(this) diff --git a/app/src/main/java/com/rosetta/messenger/ui/components/KeyboardHeightProvider.kt b/app/src/main/java/com/rosetta/messenger/ui/components/KeyboardHeightProvider.kt index 737eae4..52d88c0 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/components/KeyboardHeightProvider.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/components/KeyboardHeightProvider.kt @@ -36,11 +36,6 @@ object KeyboardHeightProvider { val savedPx = prefs.getInt(KEY_KEYBOARD_HEIGHT, defaultPx) val isDefault = savedPx == defaultPx - android.util.Log.d(TAG, "═══════════════════════════════════════") - android.util.Log.d(TAG, "📖 getSavedKeyboardHeight()") - android.util.Log.d(TAG, " 📏 Height: ${savedPx}px (${pxToDp(context, savedPx)}dp)") - android.util.Log.d(TAG, " 📦 Source: ${if (isDefault) "DEFAULT" else "SAVED"}") - android.util.Log.d(TAG, "═══════════════════════════════════════") return savedPx } @@ -48,8 +43,6 @@ object KeyboardHeightProvider { * Сохранить высоту клавиатуры */ fun saveKeyboardHeight(context: Context, heightPx: Int) { - android.util.Log.d(TAG, "═══════════════════════════════════════") - android.util.Log.d(TAG, "💾 saveKeyboardHeight($heightPx px)") if (heightPx > 0) { val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) @@ -58,19 +51,12 @@ object KeyboardHeightProvider { prefs.edit().putInt(KEY_KEYBOARD_HEIGHT, heightPx).apply() - android.util.Log.d(TAG, " 📏 New: ${heightPx}px (${pxToDp(context, heightPx)}dp)") - android.util.Log.d(TAG, " 📏 Old: ${oldHeight}px (${pxToDp(context, oldHeight)}dp)") - android.util.Log.d(TAG, " 🔄 Changed: $changed") if (changed) { - android.util.Log.d(TAG, " ✅ SAVED successfully!") } else { - android.util.Log.d(TAG, " ⏭️ Same height, no change") } } else { - android.util.Log.w(TAG, " ⚠️ INVALID height: ${heightPx}px - NOT saved!") } - android.util.Log.d(TAG, "═══════════════════════════════════════") } /** @@ -78,7 +64,6 @@ object KeyboardHeightProvider { * Telegram использует: AdjustPanLayoutHelper.keyboardDuration (250ms обычно) */ fun getKeyboardAnimationDuration(): Long { - android.util.Log.d(TAG, "⏱️ getKeyboardAnimationDuration() = 250ms") return 250L } @@ -107,9 +92,6 @@ fun rememberSavedKeyboardHeight(): Dp { val density = context.resources.displayMetrics.density val heightDp = (heightPx / density).dp - android.util.Log.d("KeyboardHeight", "🎯 rememberSavedKeyboardHeight()") - android.util.Log.d("KeyboardHeight", " 📏 Result: $heightDp (${heightPx}px)") - android.util.Log.d("KeyboardHeight", " 📱 Density: $density") return heightDp } diff --git a/app/src/main/java/com/rosetta/messenger/ui/components/OptimizedEmojiCache.kt b/app/src/main/java/com/rosetta/messenger/ui/components/OptimizedEmojiCache.kt index b470fc1..f3a2c2d 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/components/OptimizedEmojiCache.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/components/OptimizedEmojiCache.kt @@ -61,11 +61,9 @@ object OptimizedEmojiCache { loadProgress = 1f } - android.util.Log.d("EmojiCache", "✅ Предзагрузка завершена за $duration ms") isLoaded = true isPreloading = false } catch (e: Exception) { - android.util.Log.e("EmojiCache", "❌ Ошибка предзагрузки", e) allEmojis = emptyList() emojisByCategory = emptyMap() isLoaded = true @@ -85,7 +83,6 @@ object OptimizedEmojiCache { ?: emptyList() allEmojis = emojis - android.util.Log.d("EmojiCache", "📦 Загружено ${emojis.size} эмодзи") } /** @@ -125,7 +122,6 @@ object OptimizedEmojiCache { } } - android.util.Log.d("EmojiCache", "🗂️ Эмодзи сгруппированы по ${result.size} категориям") } /** @@ -140,7 +136,6 @@ object OptimizedEmojiCache { ?.take(PRELOAD_COUNT) ?: emptyList() - android.util.Log.d("EmojiCache", "🎨 Предзагружаем ${smileysToPreload.size} популярных эмодзи...") // Предзагружаем параллельно, но с ограничением val jobs = smileysToPreload.chunked(20).map { chunk -> @@ -164,7 +159,6 @@ object OptimizedEmojiCache { } jobs.awaitAll() - android.util.Log.d("EmojiCache", "✅ Предзагружено $preloadedCount изображений") isPreloading = false } diff --git a/app/src/main/java/com/rosetta/messenger/ui/components/OptimizedEmojiPicker.kt b/app/src/main/java/com/rosetta/messenger/ui/components/OptimizedEmojiPicker.kt index 38b2785..3e7d2e3 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/components/OptimizedEmojiPicker.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/components/OptimizedEmojiPicker.kt @@ -70,7 +70,6 @@ fun OptimizedEmojiPicker( // 🔥 Логирование изменений видимости LaunchedEffect(isVisible) { - android.util.Log.d("EmojiPicker", "🎭 OptimizedEmojiPicker visibility: $isVisible (height=${savedKeyboardHeight})") } // 🔥 Рендерим контент напрямую без AnimatedVisibility @@ -102,19 +101,15 @@ private fun EmojiPickerContent( var shouldRenderContent by remember { mutableStateOf(false) } LaunchedEffect(Unit) { - android.util.Log.d("EmojiPicker", "🚀 EmojiPickerContent started, keyboardHeight=$keyboardHeight") // Ждём 1 кадр чтобы анимация началась плавно kotlinx.coroutines.delay(16) // ~1 frame at 60fps shouldRenderContent = true - android.util.Log.d("EmojiPicker", "✅ Content rendering enabled after 16ms delay") // Загружаем эмодзи если еще не загружены if (!OptimizedEmojiCache.isLoaded) { - android.util.Log.d("EmojiPicker", "📦 Starting emoji preload...") OptimizedEmojiCache.preload(context) } else { - android.util.Log.d("EmojiPicker", "✅ Emojis already loaded") } } @@ -131,11 +126,9 @@ private fun EmojiPickerContent( // 🚀 При смене категории плавно скроллим наверх LaunchedEffect(selectedCategory) { - android.util.Log.d("EmojiPicker", "📂 Category changed: ${selectedCategory.key} (${displayedEmojis.size} emojis)") if (displayedEmojis.isNotEmpty()) { scope.launch { gridState.animateScrollToItem(0) - android.util.Log.v("EmojiPicker", "⬆️ Scrolled to top") } } }