diff --git a/app/src/main/java/com/rosetta/messenger/biometric/BiometricAuthManager.kt b/app/src/main/java/com/rosetta/messenger/biometric/BiometricAuthManager.kt index 9dbf05b..ae6e9b0 100644 --- a/app/src/main/java/com/rosetta/messenger/biometric/BiometricAuthManager.kt +++ b/app/src/main/java/com/rosetta/messenger/biometric/BiometricAuthManager.kt @@ -123,7 +123,6 @@ class BiometricAuthManager(private val context: Context) { Arrays.fill(passwordBytes, 0.toByte()) } } catch (e: Exception) { - Log.e(TAG, "Encryption failed", e) onError("Ошибка шифрования: ${e.message}") } }, @@ -131,11 +130,9 @@ class BiometricAuthManager(private val context: Context) { onCancel = onCancel ) } catch (e: KeyPermanentlyInvalidatedException) { - Log.e(TAG, "Key invalidated, removing and retrying", e) removeBiometricData() onError("Биометрические данные изменились. Пожалуйста, настройте заново.") } catch (e: Exception) { - Log.e(TAG, "Failed to initialize encryption", e) onError("Ошибка инициализации: ${e.message}") } } @@ -185,7 +182,6 @@ class BiometricAuthManager(private val context: Context) { decrypted = authenticatedCipher.doFinal(encrypted) onSuccess(String(decrypted, Charsets.UTF_8)) } catch (e: Exception) { - Log.e(TAG, "Decryption failed", e) onError("Ошибка расшифровки: ${e.message}") } finally { // Secure memory wipe - обнуляем расшифрованные данные @@ -196,11 +192,9 @@ class BiometricAuthManager(private val context: Context) { onCancel = onCancel ) } catch (e: KeyPermanentlyInvalidatedException) { - Log.e(TAG, "Key permanently invalidated", e) removeBiometricData() onError("Биометрические данные изменились. Пожалуйста, войдите с паролем и настройте биометрию заново.") } catch (e: Exception) { - Log.e(TAG, "Failed to initialize decryption", e) onError("Ошибка инициализации: ${e.message}") } } @@ -222,8 +216,6 @@ class BiometricAuthManager(private val context: Context) { val callback = object : BiometricPrompt.AuthenticationCallback() { override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { super.onAuthenticationError(errorCode, errString) - Log.d(TAG, "Authentication error: $errorCode - $errString") - when (errorCode) { BiometricPrompt.ERROR_USER_CANCELED, BiometricPrompt.ERROR_NEGATIVE_BUTTON, @@ -237,8 +229,6 @@ class BiometricAuthManager(private val context: Context) { override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { super.onAuthenticationSucceeded(result) - Log.d(TAG, "Authentication succeeded") - // Получаем аутентифицированный Cipher из CryptoObject val authenticatedCipher = result.cryptoObject?.cipher if (authenticatedCipher != null) { @@ -250,8 +240,7 @@ class BiometricAuthManager(private val context: Context) { override fun onAuthenticationFailed() { super.onAuthenticationFailed() - Log.d(TAG, "Authentication failed (user can retry)") - // Не вызываем onError - пользователь может попробовать снова +// Не вызываем onError - пользователь может попробовать снова } } @@ -312,7 +301,6 @@ class BiometricAuthManager(private val context: Context) { try { builder.setAttestationChallenge(generateAttestationChallenge()) } catch (e: Exception) { - Log.w(TAG, "Key attestation not supported", e) } } @@ -321,7 +309,6 @@ class BiometricAuthManager(private val context: Context) { try { builder.setIsStrongBoxBacked(true) } catch (e: Exception) { - Log.w(TAG, "StrongBox not available, using TEE", e) } } @@ -336,7 +323,6 @@ class BiometricAuthManager(private val context: Context) { return try { keyStore.getKey(KEY_ALIAS, null) as? SecretKey } catch (e: Exception) { - Log.e(TAG, "Failed to get key from Keystore", e) null } } @@ -349,10 +335,8 @@ class BiometricAuthManager(private val context: Context) { try { if (keyStore.containsAlias(KEY_ALIAS)) { keyStore.deleteEntry(KEY_ALIAS) - Log.d(TAG, "Biometric key removed from Keystore") } } catch (e: Exception) { - Log.e(TAG, "Failed to remove key from Keystore", e) } } @@ -396,7 +380,6 @@ class BiometricAuthManager(private val context: Context) { try { certificateChain[i].verify(certificateChain[i + 1].publicKey) } catch (e: Exception) { - Log.e(TAG, "Certificate chain verification failed at index $i", e) return KeyAttestationResult.Invalid("Цепочка сертификатов недействительна") } } @@ -406,14 +389,12 @@ class BiometricAuthManager(private val context: Context) { if (leafCert != null) { val attestationExtension = leafCert.getExtensionValue("1.3.6.1.4.1.11129.2.1.17") if (attestationExtension != null) { - Log.d(TAG, "Key attestation verified - key is in secure hardware") return KeyAttestationResult.Valid(isStrongBox = isKeyInStrongBox()) } } KeyAttestationResult.NotSupported } catch (e: Exception) { - Log.e(TAG, "Key attestation verification failed", e) KeyAttestationResult.Invalid(e.message ?: "Unknown error") } } @@ -437,7 +418,6 @@ class BiometricAuthManager(private val context: Context) { keyInfo.isInsideSecureHardware } } catch (e: Exception) { - Log.w(TAG, "Could not determine if key is in StrongBox", e) false } } diff --git a/app/src/main/java/com/rosetta/messenger/biometric/BiometricPreferences.kt b/app/src/main/java/com/rosetta/messenger/biometric/BiometricPreferences.kt index c5a2015..c863e72 100644 --- a/app/src/main/java/com/rosetta/messenger/biometric/BiometricPreferences.kt +++ b/app/src/main/java/com/rosetta/messenger/biometric/BiometricPreferences.kt @@ -40,7 +40,6 @@ class BiometricPreferences(private val context: Context) { try { _isBiometricEnabled.value = encryptedPrefs.getBoolean(KEY_BIOMETRIC_ENABLED, false) } catch (e: Exception) { - Log.e(TAG, "Failed to read biometric enabled state", e) } } @@ -63,7 +62,6 @@ class BiometricPreferences(private val context: Context) { EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) } catch (e: Exception) { - Log.e(TAG, "Failed to create EncryptedSharedPreferences, falling back", e) // Fallback на обычные SharedPreferences в случае ошибки (не должно произойти) return context.getSharedPreferences(PREFS_FILE_NAME + "_fallback", Context.MODE_PRIVATE) } @@ -97,7 +95,6 @@ class BiometricPreferences(private val context: Context) { suspend fun saveEncryptedPassword(publicKey: String, encryptedPassword: String) = withContext(Dispatchers.IO) { val key = "$ENCRYPTED_PASSWORD_PREFIX$publicKey" encryptedPrefs.edit().putString(key, encryptedPassword).apply() - Log.d(TAG, "Encrypted password saved for account") } /** @@ -114,7 +111,6 @@ class BiometricPreferences(private val context: Context) { suspend fun removeEncryptedPassword(publicKey: String) = withContext(Dispatchers.IO) { val key = "$ENCRYPTED_PASSWORD_PREFIX$publicKey" encryptedPrefs.edit().remove(key).apply() - Log.d(TAG, "Encrypted password removed for account") } /** @@ -123,7 +119,6 @@ class BiometricPreferences(private val context: Context) { suspend fun clearAll() = withContext(Dispatchers.IO) { encryptedPrefs.edit().clear().apply() _isBiometricEnabled.value = false - Log.d(TAG, "All biometric data cleared") } /** 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 643bf78..e1e9a59 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 @@ -347,7 +347,6 @@ fun ChatDetailScreen( // 🔥 КРИТИЧНО: Дедупликация по ID перед сортировкой! val uniqueMessages = messages.distinctBy { it.id } if (uniqueMessages.size != messages.size) { - android.util.Log.e("ChatDetailScreen", "🚨 DEDUPLICATED ${messages.size - uniqueMessages.size} messages in UI! Original: ${messages.map { it.id }}") } // Сортируем по времени (новые -> старые) для reversed layout 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 85e2011..6e4e7ea 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 @@ -264,9 +264,6 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { val allIds = newList.map { it.id } val duplicates = allIds.groupBy { it }.filter { it.value.size > 1 }.keys if (duplicates.isNotEmpty()) { - android.util.Log.e("ChatViewModel", "🚨 DUPLICATE IDS FOUND in pollLatestMessages: $duplicates") - android.util.Log.e("ChatViewModel", " currentList ids: ${currentList.map { it.id }}") - android.util.Log.e("ChatViewModel", " newMessages ids: ${newMessages.map { it.id }}") } _messages.value = newList @@ -377,13 +374,10 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { private fun addMessageSafely(message: ChatMessage): Boolean { val currentMessages = _messages.value val currentIds = currentMessages.map { it.id }.toSet() - android.util.Log.d("ChatViewModel", "🔍 addMessageSafely: id=${message.id}, currentCount=${currentMessages.size}, ids=${currentIds.take(5)}...") - if (message.id in currentIds) { - android.util.Log.e("ChatViewModel", "🚨 BLOCKED DUPLICATE: id=${message.id} already exists in ${currentIds.size} messages!") +if (message.id in currentIds) { return false } _messages.value = currentMessages + message - android.util.Log.d("ChatViewModel", "✅ Added message: id=${message.id}, newCount=${_messages.value.size}") return true } @@ -595,22 +589,17 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) { withContext(Dispatchers.Main.immediate) { val dbIds = messages.map { it.id }.toSet() val currentMsgs = _messages.value - android.util.Log.d("ChatViewModel", "📥 loadMessages: dbCount=${messages.size}, currentCount=${currentMsgs.size}") - android.util.Log.d("ChatViewModel", " DB ids: ${dbIds.take(5)}...") - android.util.Log.d("ChatViewModel", " Current ids: ${currentMsgs.map { it.id }.take(5)}...") +android.util.Log.d("ChatViewModel", " Current ids: ${currentMsgs.map { it.id }.take(5)}...") val optimisticMessages = currentMsgs.filter { msg -> msg.status == MessageStatus.SENDING && msg.id !in dbIds } - android.util.Log.d("ChatViewModel", " Optimistic (SENDING, not in DB): ${optimisticMessages.size} - ${optimisticMessages.map { it.id }}") - - val newList = messages + optimisticMessages +val newList = messages + optimisticMessages // 🔍 Финальная дедупликация по ID (на всякий случай) val deduplicatedList = newList.distinctBy { it.id } if (deduplicatedList.size != newList.size) { - android.util.Log.e("ChatViewModel", "🚨 DEDUPLICATED ${newList.size - deduplicatedList.size} messages!") } _messages.value = deduplicatedList diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/components/ImageEditorScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/components/ImageEditorScreen.kt index abdb00f..603a725 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/components/ImageEditorScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/components/ImageEditorScreen.kt @@ -242,9 +242,7 @@ fun ImageEditorScreen( lastStableKeyboardHeight = currentImeHeight } // 📊 Log IME height changes - Log.d(TAG, "⌨️ IME: height=${currentImeHeight.value}dp, wasVisible=$wasKeyboardVisible, isVisible=$isKeyboardVisible, emojiBoxVisible=${coordinator.isEmojiBoxVisible}") if (wasKeyboardVisible != isKeyboardVisible) { - Log.d(TAG, "⌨️ KEYBOARD STATE CHANGED: $wasKeyboardVisible → $isKeyboardVisible") } } } @@ -273,12 +271,8 @@ fun ImageEditorScreen( lastToggleTime = currentTime val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - - Log.d(TAG, "toggleEmojiPicker: isEmojiVisible=${coordinator.isEmojiVisible}, isEmojiBoxVisible=${coordinator.isEmojiBoxVisible}, showEmojiPicker=$showEmojiPicker") - if (coordinator.isEmojiVisible) { // EMOJI → KEYBOARD - Log.d(TAG, "TRANSITION: EMOJI → KEYBOARD") coordinator.requestShowKeyboard( showKeyboard = { editTextView?.let { editText -> @@ -290,7 +284,6 @@ fun ImageEditorScreen( ) } else { // KEYBOARD → EMOJI - Log.d(TAG, "TRANSITION: KEYBOARD → EMOJI") coordinator.requestShowEmoji( hideKeyboard = { imm.hideSoftInputFromWindow(view.windowToken, 0) }, showEmoji = { showEmojiPicker = true } @@ -1165,7 +1158,7 @@ private fun TelegramCaptionBar( } } -/** Save edited image and return the URI - crops black bars from FIT_CENTER */ +/** Save edited image and return the URI - returns original if no edits, otherwise crops black bars */ private suspend fun saveEditedImage( context: Context, photoEditor: PhotoEditor?, @@ -1174,12 +1167,15 @@ private suspend fun saveEditedImage( onResult: (Uri?) -> Unit ) { if (photoEditor == null || photoEditorView == null) { - onResult(null) + // Нет редактора - возвращаем оригинал + onResult(imageUri) return } withContext(Dispatchers.IO) { try { + // Загружаем оригинальное изображение и сохраняем его напрямую + // PhotoEditor с setClipSourceImage(true) должен обрезать черные полосы автоматически val tempFile = File(context.cacheDir, "temp_${System.currentTimeMillis()}.png") val saveSettings = SaveSettings.Builder() @@ -1187,7 +1183,6 @@ private suspend fun saveEditedImage( .setTransparencyEnabled(true) .build() - // Сохраняем полный view (с черными полосами) val savedPath = suspendCancellableCoroutine { continuation -> photoEditor.saveAsFile( tempFile.absolutePath, @@ -1204,7 +1199,8 @@ private suspend fun saveEditedImage( } if (savedPath == null) { - withContext(Dispatchers.Main) { onResult(null) } + // Ошибка сохранения - возвращаем оригинал + withContext(Dispatchers.Main) { onResult(imageUri) } return@withContext } @@ -1215,7 +1211,7 @@ private suspend fun saveEditedImage( return@withContext } - // Получаем РЕАЛЬНЫЕ размеры изображения из URI (не из drawable!) + // Получаем размеры оригинального изображения val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } context.contentResolver.openInputStream(imageUri)?.use { stream -> BitmapFactory.decodeStream(stream, null, options) @@ -1232,6 +1228,17 @@ private suspend fun saveEditedImage( val viewWidth = savedBitmap.width val viewHeight = savedBitmap.height + // Соотношение сторон оригинала и сохраненного + val originalRatio = imageWidth.toFloat() / imageHeight + val savedRatio = viewWidth.toFloat() / viewHeight + + // Если соотношения примерно равны - черных полос нет, возвращаем как есть + if (kotlin.math.abs(originalRatio - savedRatio) < 0.01f) { + savedBitmap.recycle() + withContext(Dispatchers.Main) { onResult(Uri.fromFile(tempFile)) } + return@withContext + } + // Вычисляем где находится изображение (FIT_CENTER логика) val scale = minOf( viewWidth.toFloat() / imageWidth, @@ -1244,13 +1251,15 @@ private suspend fun saveEditedImage( val left = ((viewWidth - scaledWidth) / 2).coerceAtLeast(0) val top = ((viewHeight - scaledHeight) / 2).coerceAtLeast(0) - // Обрезаем черные полосы + val cropWidth = scaledWidth.coerceIn(1, viewWidth - left) + val cropHeight = scaledHeight.coerceIn(1, viewHeight - top) +// Обрезаем черные полосы val croppedBitmap = Bitmap.createBitmap( savedBitmap, left, top, - scaledWidth.coerceAtMost(viewWidth - left), - scaledHeight.coerceAtMost(viewHeight - top) + cropWidth, + cropHeight ) // Сохраняем обрезанное изображение @@ -1267,19 +1276,23 @@ private suspend fun saveEditedImage( onResult(Uri.fromFile(finalFile)) } } catch (e: Exception) { - withContext(Dispatchers.Main) { onResult(null) } + // При ошибке возвращаем оригинал + withContext(Dispatchers.Main) { onResult(imageUri) } } } } -/** Save edited image synchronously - crops black bars from FIT_CENTER */ +/** Save edited image synchronously - returns original if no edits, otherwise crops black bars */ private suspend fun saveEditedImageSync( context: Context, photoEditor: PhotoEditor?, photoEditorView: PhotoEditorView?, imageUri: Uri ): Uri? { - if (photoEditor == null || photoEditorView == null) return null + if (photoEditor == null || photoEditorView == null) { + // Нет редактора - возвращаем оригинал + return imageUri + } return withContext(Dispatchers.IO) { try { @@ -1308,13 +1321,16 @@ private suspend fun saveEditedImageSync( ) } - if (savedPath == null) return@withContext null + if (savedPath == null) { + // Ошибка - возвращаем оригинал + return@withContext imageUri + } // Загружаем сохраненное изображение val savedBitmap = BitmapFactory.decodeFile(savedPath) ?: return@withContext Uri.fromFile(tempFile) - // Получаем РЕАЛЬНЫЕ размеры изображения из URI (не из drawable!) + // Получаем размеры оригинального изображения val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } context.contentResolver.openInputStream(imageUri)?.use { stream -> BitmapFactory.decodeStream(stream, null, options) @@ -1330,6 +1346,16 @@ private suspend fun saveEditedImageSync( val viewWidth = savedBitmap.width val viewHeight = savedBitmap.height + // Соотношение сторон оригинала и сохраненного + val originalRatio = imageWidth.toFloat() / imageHeight + val savedRatio = viewWidth.toFloat() / viewHeight + + // Если соотношения примерно равны - черных полос нет + if (kotlin.math.abs(originalRatio - savedRatio) < 0.01f) { + savedBitmap.recycle() + return@withContext Uri.fromFile(tempFile) + } + // Вычисляем где находится изображение (FIT_CENTER логика) val scale = minOf( viewWidth.toFloat() / imageWidth, @@ -1342,13 +1368,15 @@ private suspend fun saveEditedImageSync( val left = ((viewWidth - scaledWidth) / 2).coerceAtLeast(0) val top = ((viewHeight - scaledHeight) / 2).coerceAtLeast(0) - // Обрезаем черные полосы + val cropWidth = scaledWidth.coerceIn(1, viewWidth - left) + val cropHeight = scaledHeight.coerceIn(1, viewHeight - top) +// Обрезаем черные полосы val croppedBitmap = Bitmap.createBitmap( savedBitmap, left, top, - scaledWidth.coerceAtMost(viewWidth - left), - scaledHeight.coerceAtMost(viewHeight - top) + cropWidth, + cropHeight ) // Сохраняем обрезанное изображение @@ -1366,7 +1394,8 @@ private suspend fun saveEditedImageSync( Uri.fromFile(finalFile) } catch (e: Exception) { - null + // При ошибке возвращаем оригинал + imageUri } } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/components/MediaPickerBottomSheet.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/components/MediaPickerBottomSheet.kt index 9282661..d6b336f 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/components/MediaPickerBottomSheet.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/components/MediaPickerBottomSheet.kt @@ -1440,7 +1440,6 @@ fun PhotoPreviewWithCaptionScreen( coordinator.syncHeights() lastStableKeyboardHeight = currentImeHeight } - Log.d("PhotoPreview", "IME height: ${currentImeHeight.value}dp, isKeyboardVisible: $isKeyboardVisible, emojiHeight: ${coordinator.emojiHeight.value}dp, isEmojiBoxVisible: ${coordinator.isEmojiBoxVisible}") } } @@ -1464,7 +1463,6 @@ fun PhotoPreviewWithCaptionScreen( fun toggleEmojiPicker() { val currentTime = System.currentTimeMillis() if (currentTime - lastToggleTime < toggleCooldownMs) { - Log.d("PhotoPreview", "Toggle blocked by cooldown") return } lastToggleTime = currentTime @@ -1473,30 +1471,24 @@ fun PhotoPreviewWithCaptionScreen( if (coordinator.isEmojiVisible) { // EMOJI → KEYBOARD - Log.d("PhotoPreview", "TOGGLE: Emoji → Keyboard") coordinator.requestShowKeyboard( showKeyboard = { - Log.d("PhotoPreview", "Showing keyboard...") editTextView?.let { editText -> editText.requestFocus() imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT) } }, hideEmoji = { - Log.d("PhotoPreview", "Hiding emoji picker") showEmojiPicker = false } ) } else { // KEYBOARD → EMOJI - Log.d("PhotoPreview", "TOGGLE: Keyboard → Emoji") coordinator.requestShowEmoji( hideKeyboard = { - Log.d("PhotoPreview", "Hiding keyboard...") imm.hideSoftInputFromWindow(view.windowToken, 0) }, showEmoji = { - Log.d("PhotoPreview", "Showing emoji picker") showEmojiPicker = true } ) @@ -1508,8 +1500,6 @@ fun PhotoPreviewWithCaptionScreen( val shouldAddNavBarPadding = !isKeyboardVisible && !coordinator.isEmojiBoxVisible // Логируем состояние при каждой рекомпозиции - Log.d("PhotoPreview", "RENDER: showEmoji=$showEmojiPicker, isKeyboard=$isKeyboardVisible, isEmojiBoxVisible=${coordinator.isEmojiBoxVisible}, useImePadding=$shouldUseImePadding, emojiHeight=${coordinator.emojiHeight.value}dp") - Surface( color = backgroundColor, modifier = Modifier.fillMaxSize() diff --git a/app/src/main/java/com/rosetta/messenger/ui/components/metaball/ProfileMetaballOverlay.kt b/app/src/main/java/com/rosetta/messenger/ui/components/metaball/ProfileMetaballOverlay.kt index 412ae3c..fc3356d 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/components/metaball/ProfileMetaballOverlay.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/components/metaball/ProfileMetaballOverlay.kt @@ -286,9 +286,6 @@ private fun computeAvatarState( val showBlob = collapseProgress < ProfileMetaballConstants.MERGE_COMPLETE_PROGRESS && radius > 1f // DEBUG LOG - Log.d("Metaball", "collapse=$collapseProgress, expansion=$expansionProgress, radius=$radius, diff=$diff") - Log.d("Metaball", "centerY=$centerY, cornerRadius=$cornerRadius, isDrawing=$isDrawing, isNear=$isNear") - return AvatarState( centerX = centerX, centerY = centerY, diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt index 20eec79..812b3d1 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt @@ -259,8 +259,6 @@ fun OtherProfileScreen( } // DEBUG LOGS - Log.d("OtherProfileScroll", "expansionProgress=$expansionProgress, isPulledDown=$isPulledDown, isDragging=$isDragging") - // ═══════════════════════════════════════════════════════════════ // NESTED SCROLL - Telegram style with overscroll support // ═══════════════════════════════════════════════════════════════ diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/ProfilePhotoPicker.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/ProfilePhotoPicker.kt index e3e6941..fee1416 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/settings/ProfilePhotoPicker.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/ProfilePhotoPicker.kt @@ -588,7 +588,6 @@ private suspend fun loadPhotos(context: Context): List = withContext( } } } catch (e: Exception) { - android.util.Log.e(TAG, "Error loading photos: ${e.message}", e) } items diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt index 43ec4d6..bc3e2f2 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt @@ -401,8 +401,6 @@ fun ProfileScreen( } // DEBUG LOGS - Log.d("ProfileScroll", "expansionProgress=$expansionProgress, isPulledDown=$isPulledDown, isDragging=$isDragging") - // ═══════════════════════════════════════════════════════════════ // NESTED SCROLL - Telegram style // ═══════════════════════════════════════════════════════════════ diff --git a/remove_logs.py b/remove_logs.py new file mode 100644 index 0000000..6efe288 --- /dev/null +++ b/remove_logs.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +""" +Скрипт для удаления Log.d/e/w/i/v вызовов из Kotlin файлов +""" + +import os +import re +import sys + +def remove_logs_from_file(filepath): + """Удаляет Log.d/e/w/i/v вызовы из файла""" + + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + original_content = content + + # Паттерн для Log.d/e/w/i/v с учетом многострочных вызовов + # Матчит: Log.d("TAG", "message") или Log.d(TAG, "message", exception) + patterns = [ + # Простые однострочные логи + r'^\s*Log\.[dewiv]\([^)]*\)\s*\n', + # Многострочные логи (с переносами внутри скобок) + r'^\s*Log\.[dewiv]\([^)]*\n[^)]*\)\s*\n', + r'^\s*Log\.[dewiv]\([^)]*\n[^)]*\n[^)]*\)\s*\n', + r'^\s*Log\.[dewiv]\([^)]*\n[^)]*\n[^)]*\n[^)]*\)\s*\n', + # android.util.Log + r'^\s*android\.util\.Log\.[dewiv]\([^)]*\)\s*\n', + ] + + for pattern in patterns: + content = re.sub(pattern, '', content, flags=re.MULTILINE) + + # Более агрессивный паттерн для оставшихся логов + # Находит Log.X( и удаляет до закрывающей скобки + def remove_log_call(match): + return '' + + # Паттерн который находит Log.X(...) учитывая вложенные скобки + log_pattern = r'^\s*(?:android\.util\.)?Log\.[dewiv]\s*\([^()]*(?:\([^()]*\)[^()]*)*\)\s*\n?' + content = re.sub(log_pattern, '', content, flags=re.MULTILINE) + + if content != original_content: + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + return True + return False + +def find_kotlin_files(directory): + """Находит все .kt файлы в директории""" + kotlin_files = [] + for root, dirs, files in os.walk(directory): + # Пропускаем build директории + dirs[:] = [d for d in dirs if d not in ['build', '.gradle', '.idea']] + for file in files: + if file.endswith('.kt'): + kotlin_files.append(os.path.join(root, file)) + return kotlin_files + +def count_logs_in_file(filepath): + """Считает количество Log вызовов в файле""" + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + pattern = r'(?:android\.util\.)?Log\.[dewiv]\s*\(' + matches = re.findall(pattern, content) + return len(matches) + +def main(): + # Директория с исходниками + src_dir = '/Users/ruslanmakhmatov/Desktop/Work/rosette-app/rosetta-android/app/src/main/java/com/rosetta/messenger' + + if not os.path.exists(src_dir): + print(f"Директория не найдена: {src_dir}") + sys.exit(1) + + kotlin_files = find_kotlin_files(src_dir) + print(f"Найдено {len(kotlin_files)} Kotlin файлов") + + # Сначала считаем логи + total_logs_before = 0 + files_with_logs = [] + for filepath in kotlin_files: + count = count_logs_in_file(filepath) + if count > 0: + total_logs_before += count + files_with_logs.append((filepath, count)) + + print(f"Найдено {total_logs_before} Log вызовов в {len(files_with_logs)} файлах") + + if '--dry-run' in sys.argv: + print("\n[DRY RUN] Файлы с логами:") + for filepath, count in sorted(files_with_logs, key=lambda x: -x[1]): + print(f" {count:3d} логов: {os.path.basename(filepath)}") + return + + # Удаляем логи + modified_count = 0 + for filepath in kotlin_files: + if remove_logs_from_file(filepath): + modified_count += 1 + print(f"✓ {os.path.basename(filepath)}") + + # Считаем оставшиеся + total_logs_after = 0 + for filepath in kotlin_files: + total_logs_after += count_logs_in_file(filepath) + + print(f"\n{'='*50}") + print(f"Изменено файлов: {modified_count}") + print(f"Логов до: {total_logs_before}") + print(f"Логов после: {total_logs_after}") + print(f"Удалено: {total_logs_before - total_logs_after}") + + if total_logs_after > 0: + print(f"\n⚠️ Осталось {total_logs_after} логов (возможно сложные многострочные)") + +if __name__ == '__main__': + main()