fix: remove logs

This commit is contained in:
k1ngsterr1
2026-02-03 22:19:01 +05:00
parent 6bb0a90ea0
commit a7268bb986
11 changed files with 176 additions and 83 deletions

View File

@@ -123,7 +123,6 @@ class BiometricAuthManager(private val context: Context) {
Arrays.fill(passwordBytes, 0.toByte()) Arrays.fill(passwordBytes, 0.toByte())
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Encryption failed", e)
onError("Ошибка шифрования: ${e.message}") onError("Ошибка шифрования: ${e.message}")
} }
}, },
@@ -131,11 +130,9 @@ class BiometricAuthManager(private val context: Context) {
onCancel = onCancel onCancel = onCancel
) )
} catch (e: KeyPermanentlyInvalidatedException) { } catch (e: KeyPermanentlyInvalidatedException) {
Log.e(TAG, "Key invalidated, removing and retrying", e)
removeBiometricData() removeBiometricData()
onError("Биометрические данные изменились. Пожалуйста, настройте заново.") onError("Биометрические данные изменились. Пожалуйста, настройте заново.")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Failed to initialize encryption", e)
onError("Ошибка инициализации: ${e.message}") onError("Ошибка инициализации: ${e.message}")
} }
} }
@@ -185,7 +182,6 @@ class BiometricAuthManager(private val context: Context) {
decrypted = authenticatedCipher.doFinal(encrypted) decrypted = authenticatedCipher.doFinal(encrypted)
onSuccess(String(decrypted, Charsets.UTF_8)) onSuccess(String(decrypted, Charsets.UTF_8))
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Decryption failed", e)
onError("Ошибка расшифровки: ${e.message}") onError("Ошибка расшифровки: ${e.message}")
} finally { } finally {
// Secure memory wipe - обнуляем расшифрованные данные // Secure memory wipe - обнуляем расшифрованные данные
@@ -196,11 +192,9 @@ class BiometricAuthManager(private val context: Context) {
onCancel = onCancel onCancel = onCancel
) )
} catch (e: KeyPermanentlyInvalidatedException) { } catch (e: KeyPermanentlyInvalidatedException) {
Log.e(TAG, "Key permanently invalidated", e)
removeBiometricData() removeBiometricData()
onError("Биометрические данные изменились. Пожалуйста, войдите с паролем и настройте биометрию заново.") onError("Биометрические данные изменились. Пожалуйста, войдите с паролем и настройте биометрию заново.")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Failed to initialize decryption", e)
onError("Ошибка инициализации: ${e.message}") onError("Ошибка инициализации: ${e.message}")
} }
} }
@@ -222,8 +216,6 @@ class BiometricAuthManager(private val context: Context) {
val callback = object : BiometricPrompt.AuthenticationCallback() { val callback = object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString) super.onAuthenticationError(errorCode, errString)
Log.d(TAG, "Authentication error: $errorCode - $errString")
when (errorCode) { when (errorCode) {
BiometricPrompt.ERROR_USER_CANCELED, BiometricPrompt.ERROR_USER_CANCELED,
BiometricPrompt.ERROR_NEGATIVE_BUTTON, BiometricPrompt.ERROR_NEGATIVE_BUTTON,
@@ -237,8 +229,6 @@ class BiometricAuthManager(private val context: Context) {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result) super.onAuthenticationSucceeded(result)
Log.d(TAG, "Authentication succeeded")
// Получаем аутентифицированный Cipher из CryptoObject // Получаем аутентифицированный Cipher из CryptoObject
val authenticatedCipher = result.cryptoObject?.cipher val authenticatedCipher = result.cryptoObject?.cipher
if (authenticatedCipher != null) { if (authenticatedCipher != null) {
@@ -250,8 +240,7 @@ class BiometricAuthManager(private val context: Context) {
override fun onAuthenticationFailed() { override fun onAuthenticationFailed() {
super.onAuthenticationFailed() super.onAuthenticationFailed()
Log.d(TAG, "Authentication failed (user can retry)") // Не вызываем onError - пользователь может попробовать снова
// Не вызываем onError - пользователь может попробовать снова
} }
} }
@@ -312,7 +301,6 @@ class BiometricAuthManager(private val context: Context) {
try { try {
builder.setAttestationChallenge(generateAttestationChallenge()) builder.setAttestationChallenge(generateAttestationChallenge())
} catch (e: Exception) { } catch (e: Exception) {
Log.w(TAG, "Key attestation not supported", e)
} }
} }
@@ -321,7 +309,6 @@ class BiometricAuthManager(private val context: Context) {
try { try {
builder.setIsStrongBoxBacked(true) builder.setIsStrongBoxBacked(true)
} catch (e: Exception) { } catch (e: Exception) {
Log.w(TAG, "StrongBox not available, using TEE", e)
} }
} }
@@ -336,7 +323,6 @@ class BiometricAuthManager(private val context: Context) {
return try { return try {
keyStore.getKey(KEY_ALIAS, null) as? SecretKey keyStore.getKey(KEY_ALIAS, null) as? SecretKey
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Failed to get key from Keystore", e)
null null
} }
} }
@@ -349,10 +335,8 @@ class BiometricAuthManager(private val context: Context) {
try { try {
if (keyStore.containsAlias(KEY_ALIAS)) { if (keyStore.containsAlias(KEY_ALIAS)) {
keyStore.deleteEntry(KEY_ALIAS) keyStore.deleteEntry(KEY_ALIAS)
Log.d(TAG, "Biometric key removed from Keystore")
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Failed to remove key from Keystore", e)
} }
} }
@@ -396,7 +380,6 @@ class BiometricAuthManager(private val context: Context) {
try { try {
certificateChain[i].verify(certificateChain[i + 1].publicKey) certificateChain[i].verify(certificateChain[i + 1].publicKey)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Certificate chain verification failed at index $i", e)
return KeyAttestationResult.Invalid("Цепочка сертификатов недействительна") return KeyAttestationResult.Invalid("Цепочка сертификатов недействительна")
} }
} }
@@ -406,14 +389,12 @@ class BiometricAuthManager(private val context: Context) {
if (leafCert != null) { if (leafCert != null) {
val attestationExtension = leafCert.getExtensionValue("1.3.6.1.4.1.11129.2.1.17") val attestationExtension = leafCert.getExtensionValue("1.3.6.1.4.1.11129.2.1.17")
if (attestationExtension != null) { if (attestationExtension != null) {
Log.d(TAG, "Key attestation verified - key is in secure hardware")
return KeyAttestationResult.Valid(isStrongBox = isKeyInStrongBox()) return KeyAttestationResult.Valid(isStrongBox = isKeyInStrongBox())
} }
} }
KeyAttestationResult.NotSupported KeyAttestationResult.NotSupported
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Key attestation verification failed", e)
KeyAttestationResult.Invalid(e.message ?: "Unknown error") KeyAttestationResult.Invalid(e.message ?: "Unknown error")
} }
} }
@@ -437,7 +418,6 @@ class BiometricAuthManager(private val context: Context) {
keyInfo.isInsideSecureHardware keyInfo.isInsideSecureHardware
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.w(TAG, "Could not determine if key is in StrongBox", e)
false false
} }
} }

View File

@@ -40,7 +40,6 @@ class BiometricPreferences(private val context: Context) {
try { try {
_isBiometricEnabled.value = encryptedPrefs.getBoolean(KEY_BIOMETRIC_ENABLED, false) _isBiometricEnabled.value = encryptedPrefs.getBoolean(KEY_BIOMETRIC_ENABLED, false)
} catch (e: Exception) { } 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 EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
) )
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Failed to create EncryptedSharedPreferences, falling back", e)
// Fallback на обычные SharedPreferences в случае ошибки (не должно произойти) // Fallback на обычные SharedPreferences в случае ошибки (не должно произойти)
return context.getSharedPreferences(PREFS_FILE_NAME + "_fallback", Context.MODE_PRIVATE) 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) { suspend fun saveEncryptedPassword(publicKey: String, encryptedPassword: String) = withContext(Dispatchers.IO) {
val key = "$ENCRYPTED_PASSWORD_PREFIX$publicKey" val key = "$ENCRYPTED_PASSWORD_PREFIX$publicKey"
encryptedPrefs.edit().putString(key, encryptedPassword).apply() 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) { suspend fun removeEncryptedPassword(publicKey: String) = withContext(Dispatchers.IO) {
val key = "$ENCRYPTED_PASSWORD_PREFIX$publicKey" val key = "$ENCRYPTED_PASSWORD_PREFIX$publicKey"
encryptedPrefs.edit().remove(key).apply() 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) { suspend fun clearAll() = withContext(Dispatchers.IO) {
encryptedPrefs.edit().clear().apply() encryptedPrefs.edit().clear().apply()
_isBiometricEnabled.value = false _isBiometricEnabled.value = false
Log.d(TAG, "All biometric data cleared")
} }
/** /**

View File

@@ -347,7 +347,6 @@ fun ChatDetailScreen(
// 🔥 КРИТИЧНО: Дедупликация по ID перед сортировкой! // 🔥 КРИТИЧНО: Дедупликация по ID перед сортировкой!
val uniqueMessages = messages.distinctBy { it.id } val uniqueMessages = messages.distinctBy { it.id }
if (uniqueMessages.size != messages.size) { 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 // Сортируем по времени (новые -> старые) для reversed layout

View File

@@ -264,9 +264,6 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
val allIds = newList.map { it.id } val allIds = newList.map { it.id }
val duplicates = allIds.groupBy { it }.filter { it.value.size > 1 }.keys val duplicates = allIds.groupBy { it }.filter { it.value.size > 1 }.keys
if (duplicates.isNotEmpty()) { 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 _messages.value = newList
@@ -377,13 +374,10 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
private fun addMessageSafely(message: ChatMessage): Boolean { private fun addMessageSafely(message: ChatMessage): Boolean {
val currentMessages = _messages.value val currentMessages = _messages.value
val currentIds = currentMessages.map { it.id }.toSet() 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) {
if (message.id in currentIds) {
android.util.Log.e("ChatViewModel", "🚨 BLOCKED DUPLICATE: id=${message.id} already exists in ${currentIds.size} messages!")
return false return false
} }
_messages.value = currentMessages + message _messages.value = currentMessages + message
android.util.Log.d("ChatViewModel", "✅ Added message: id=${message.id}, newCount=${_messages.value.size}")
return true return true
} }
@@ -595,22 +589,17 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
withContext(Dispatchers.Main.immediate) { withContext(Dispatchers.Main.immediate) {
val dbIds = messages.map { it.id }.toSet() val dbIds = messages.map { it.id }.toSet()
val currentMsgs = _messages.value val currentMsgs = _messages.value
android.util.Log.d("ChatViewModel", "📥 loadMessages: dbCount=${messages.size}, currentCount=${currentMsgs.size}") android.util.Log.d("ChatViewModel", " Current ids: ${currentMsgs.map { it.id }.take(5)}...")
android.util.Log.d("ChatViewModel", " DB ids: ${dbIds.take(5)}...")
android.util.Log.d("ChatViewModel", " Current ids: ${currentMsgs.map { it.id }.take(5)}...")
val optimisticMessages = currentMsgs.filter { msg -> val optimisticMessages = currentMsgs.filter { msg ->
msg.status == MessageStatus.SENDING && msg.id !in dbIds 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 (на всякий случай) // 🔍 Финальная дедупликация по ID (на всякий случай)
val deduplicatedList = newList.distinctBy { it.id } val deduplicatedList = newList.distinctBy { it.id }
if (deduplicatedList.size != newList.size) { if (deduplicatedList.size != newList.size) {
android.util.Log.e("ChatViewModel", "🚨 DEDUPLICATED ${newList.size - deduplicatedList.size} messages!")
} }
_messages.value = deduplicatedList _messages.value = deduplicatedList

View File

@@ -242,9 +242,7 @@ fun ImageEditorScreen(
lastStableKeyboardHeight = currentImeHeight lastStableKeyboardHeight = currentImeHeight
} }
// 📊 Log IME height changes // 📊 Log IME height changes
Log.d(TAG, "⌨️ IME: height=${currentImeHeight.value}dp, wasVisible=$wasKeyboardVisible, isVisible=$isKeyboardVisible, emojiBoxVisible=${coordinator.isEmojiBoxVisible}")
if (wasKeyboardVisible != isKeyboardVisible) { if (wasKeyboardVisible != isKeyboardVisible) {
Log.d(TAG, "⌨️ KEYBOARD STATE CHANGED: $wasKeyboardVisible$isKeyboardVisible")
} }
} }
} }
@@ -273,12 +271,8 @@ fun ImageEditorScreen(
lastToggleTime = currentTime lastToggleTime = currentTime
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager 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) { if (coordinator.isEmojiVisible) {
// EMOJI → KEYBOARD // EMOJI → KEYBOARD
Log.d(TAG, "TRANSITION: EMOJI → KEYBOARD")
coordinator.requestShowKeyboard( coordinator.requestShowKeyboard(
showKeyboard = { showKeyboard = {
editTextView?.let { editText -> editTextView?.let { editText ->
@@ -290,7 +284,6 @@ fun ImageEditorScreen(
) )
} else { } else {
// KEYBOARD → EMOJI // KEYBOARD → EMOJI
Log.d(TAG, "TRANSITION: KEYBOARD → EMOJI")
coordinator.requestShowEmoji( coordinator.requestShowEmoji(
hideKeyboard = { imm.hideSoftInputFromWindow(view.windowToken, 0) }, hideKeyboard = { imm.hideSoftInputFromWindow(view.windowToken, 0) },
showEmoji = { showEmojiPicker = true } 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( private suspend fun saveEditedImage(
context: Context, context: Context,
photoEditor: PhotoEditor?, photoEditor: PhotoEditor?,
@@ -1174,12 +1167,15 @@ private suspend fun saveEditedImage(
onResult: (Uri?) -> Unit onResult: (Uri?) -> Unit
) { ) {
if (photoEditor == null || photoEditorView == null) { if (photoEditor == null || photoEditorView == null) {
onResult(null) // Нет редактора - возвращаем оригинал
onResult(imageUri)
return return
} }
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
try { try {
// Загружаем оригинальное изображение и сохраняем его напрямую
// PhotoEditor с setClipSourceImage(true) должен обрезать черные полосы автоматически
val tempFile = File(context.cacheDir, "temp_${System.currentTimeMillis()}.png") val tempFile = File(context.cacheDir, "temp_${System.currentTimeMillis()}.png")
val saveSettings = SaveSettings.Builder() val saveSettings = SaveSettings.Builder()
@@ -1187,7 +1183,6 @@ private suspend fun saveEditedImage(
.setTransparencyEnabled(true) .setTransparencyEnabled(true)
.build() .build()
// Сохраняем полный view (с черными полосами)
val savedPath = suspendCancellableCoroutine<String?> { continuation -> val savedPath = suspendCancellableCoroutine<String?> { continuation ->
photoEditor.saveAsFile( photoEditor.saveAsFile(
tempFile.absolutePath, tempFile.absolutePath,
@@ -1204,7 +1199,8 @@ private suspend fun saveEditedImage(
} }
if (savedPath == null) { if (savedPath == null) {
withContext(Dispatchers.Main) { onResult(null) } // Ошибка сохранения - возвращаем оригинал
withContext(Dispatchers.Main) { onResult(imageUri) }
return@withContext return@withContext
} }
@@ -1215,7 +1211,7 @@ private suspend fun saveEditedImage(
return@withContext return@withContext
} }
// Получаем РЕАЛЬНЫЕ размеры изображения из URI (не из drawable!) // Получаем размеры оригинального изображения
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
context.contentResolver.openInputStream(imageUri)?.use { stream -> context.contentResolver.openInputStream(imageUri)?.use { stream ->
BitmapFactory.decodeStream(stream, null, options) BitmapFactory.decodeStream(stream, null, options)
@@ -1232,6 +1228,17 @@ private suspend fun saveEditedImage(
val viewWidth = savedBitmap.width val viewWidth = savedBitmap.width
val viewHeight = savedBitmap.height 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 логика) // Вычисляем где находится изображение (FIT_CENTER логика)
val scale = minOf( val scale = minOf(
viewWidth.toFloat() / imageWidth, viewWidth.toFloat() / imageWidth,
@@ -1244,13 +1251,15 @@ private suspend fun saveEditedImage(
val left = ((viewWidth - scaledWidth) / 2).coerceAtLeast(0) val left = ((viewWidth - scaledWidth) / 2).coerceAtLeast(0)
val top = ((viewHeight - scaledHeight) / 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( val croppedBitmap = Bitmap.createBitmap(
savedBitmap, savedBitmap,
left, left,
top, top,
scaledWidth.coerceAtMost(viewWidth - left), cropWidth,
scaledHeight.coerceAtMost(viewHeight - top) cropHeight
) )
// Сохраняем обрезанное изображение // Сохраняем обрезанное изображение
@@ -1267,19 +1276,23 @@ private suspend fun saveEditedImage(
onResult(Uri.fromFile(finalFile)) onResult(Uri.fromFile(finalFile))
} }
} catch (e: Exception) { } 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( private suspend fun saveEditedImageSync(
context: Context, context: Context,
photoEditor: PhotoEditor?, photoEditor: PhotoEditor?,
photoEditorView: PhotoEditorView?, photoEditorView: PhotoEditorView?,
imageUri: Uri imageUri: Uri
): Uri? { ): Uri? {
if (photoEditor == null || photoEditorView == null) return null if (photoEditor == null || photoEditorView == null) {
// Нет редактора - возвращаем оригинал
return imageUri
}
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
try { 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) val savedBitmap = BitmapFactory.decodeFile(savedPath)
?: return@withContext Uri.fromFile(tempFile) ?: return@withContext Uri.fromFile(tempFile)
// Получаем РЕАЛЬНЫЕ размеры изображения из URI (не из drawable!) // Получаем размеры оригинального изображения
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
context.contentResolver.openInputStream(imageUri)?.use { stream -> context.contentResolver.openInputStream(imageUri)?.use { stream ->
BitmapFactory.decodeStream(stream, null, options) BitmapFactory.decodeStream(stream, null, options)
@@ -1330,6 +1346,16 @@ private suspend fun saveEditedImageSync(
val viewWidth = savedBitmap.width val viewWidth = savedBitmap.width
val viewHeight = savedBitmap.height 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 логика) // Вычисляем где находится изображение (FIT_CENTER логика)
val scale = minOf( val scale = minOf(
viewWidth.toFloat() / imageWidth, viewWidth.toFloat() / imageWidth,
@@ -1342,13 +1368,15 @@ private suspend fun saveEditedImageSync(
val left = ((viewWidth - scaledWidth) / 2).coerceAtLeast(0) val left = ((viewWidth - scaledWidth) / 2).coerceAtLeast(0)
val top = ((viewHeight - scaledHeight) / 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( val croppedBitmap = Bitmap.createBitmap(
savedBitmap, savedBitmap,
left, left,
top, top,
scaledWidth.coerceAtMost(viewWidth - left), cropWidth,
scaledHeight.coerceAtMost(viewHeight - top) cropHeight
) )
// Сохраняем обрезанное изображение // Сохраняем обрезанное изображение
@@ -1366,7 +1394,8 @@ private suspend fun saveEditedImageSync(
Uri.fromFile(finalFile) Uri.fromFile(finalFile)
} catch (e: Exception) { } catch (e: Exception) {
null // При ошибке возвращаем оригинал
imageUri
} }
} }
} }

View File

@@ -1440,7 +1440,6 @@ fun PhotoPreviewWithCaptionScreen(
coordinator.syncHeights() coordinator.syncHeights()
lastStableKeyboardHeight = currentImeHeight 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() { fun toggleEmojiPicker() {
val currentTime = System.currentTimeMillis() val currentTime = System.currentTimeMillis()
if (currentTime - lastToggleTime < toggleCooldownMs) { if (currentTime - lastToggleTime < toggleCooldownMs) {
Log.d("PhotoPreview", "Toggle blocked by cooldown")
return return
} }
lastToggleTime = currentTime lastToggleTime = currentTime
@@ -1473,30 +1471,24 @@ fun PhotoPreviewWithCaptionScreen(
if (coordinator.isEmojiVisible) { if (coordinator.isEmojiVisible) {
// EMOJI → KEYBOARD // EMOJI → KEYBOARD
Log.d("PhotoPreview", "TOGGLE: Emoji → Keyboard")
coordinator.requestShowKeyboard( coordinator.requestShowKeyboard(
showKeyboard = { showKeyboard = {
Log.d("PhotoPreview", "Showing keyboard...")
editTextView?.let { editText -> editTextView?.let { editText ->
editText.requestFocus() editText.requestFocus()
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT) imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT)
} }
}, },
hideEmoji = { hideEmoji = {
Log.d("PhotoPreview", "Hiding emoji picker")
showEmojiPicker = false showEmojiPicker = false
} }
) )
} else { } else {
// KEYBOARD → EMOJI // KEYBOARD → EMOJI
Log.d("PhotoPreview", "TOGGLE: Keyboard → Emoji")
coordinator.requestShowEmoji( coordinator.requestShowEmoji(
hideKeyboard = { hideKeyboard = {
Log.d("PhotoPreview", "Hiding keyboard...")
imm.hideSoftInputFromWindow(view.windowToken, 0) imm.hideSoftInputFromWindow(view.windowToken, 0)
}, },
showEmoji = { showEmoji = {
Log.d("PhotoPreview", "Showing emoji picker")
showEmojiPicker = true showEmojiPicker = true
} }
) )
@@ -1508,8 +1500,6 @@ fun PhotoPreviewWithCaptionScreen(
val shouldAddNavBarPadding = !isKeyboardVisible && !coordinator.isEmojiBoxVisible val shouldAddNavBarPadding = !isKeyboardVisible && !coordinator.isEmojiBoxVisible
// Логируем состояние при каждой рекомпозиции // Логируем состояние при каждой рекомпозиции
Log.d("PhotoPreview", "RENDER: showEmoji=$showEmojiPicker, isKeyboard=$isKeyboardVisible, isEmojiBoxVisible=${coordinator.isEmojiBoxVisible}, useImePadding=$shouldUseImePadding, emojiHeight=${coordinator.emojiHeight.value}dp")
Surface( Surface(
color = backgroundColor, color = backgroundColor,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()

View File

@@ -286,9 +286,6 @@ private fun computeAvatarState(
val showBlob = collapseProgress < ProfileMetaballConstants.MERGE_COMPLETE_PROGRESS && radius > 1f val showBlob = collapseProgress < ProfileMetaballConstants.MERGE_COMPLETE_PROGRESS && radius > 1f
// DEBUG LOG // 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( return AvatarState(
centerX = centerX, centerX = centerX,
centerY = centerY, centerY = centerY,

View File

@@ -259,8 +259,6 @@ fun OtherProfileScreen(
} }
// DEBUG LOGS // DEBUG LOGS
Log.d("OtherProfileScroll", "expansionProgress=$expansionProgress, isPulledDown=$isPulledDown, isDragging=$isDragging")
// ═══════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════
// NESTED SCROLL - Telegram style with overscroll support // NESTED SCROLL - Telegram style with overscroll support
// ═══════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════

View File

@@ -588,7 +588,6 @@ private suspend fun loadPhotos(context: Context): List<PhotoItem> = withContext(
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
android.util.Log.e(TAG, "Error loading photos: ${e.message}", e)
} }
items items

View File

@@ -401,8 +401,6 @@ fun ProfileScreen(
} }
// DEBUG LOGS // DEBUG LOGS
Log.d("ProfileScroll", "expansionProgress=$expansionProgress, isPulledDown=$isPulledDown, isDragging=$isDragging")
// ═══════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════
// NESTED SCROLL - Telegram style // NESTED SCROLL - Telegram style
// ═══════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════

119
remove_logs.py Normal file
View File

@@ -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()