feat: Improve keyboard handling in selection mode for better user experience

This commit is contained in:
k1ngsterr1
2026-01-15 20:54:04 +05:00
parent 2b1b6eecef
commit 22a17e5fec
3 changed files with 278 additions and 249 deletions

View File

@@ -323,15 +323,22 @@ fun ChatDetailScreen(
var selectedMessages by remember { mutableStateOf<Set<String>>(emptySet()) } var selectedMessages by remember { mutableStateOf<Set<String>>(emptySet()) }
val isSelectionMode = selectedMessages.isNotEmpty() val isSelectionMode = selectedMessages.isNotEmpty()
// 🔥 Закрываем клавиатуру и emoji picker когда открывается selection mode (action bar с Reply/Forward) // Логирование изменений 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) { LaunchedEffect(isSelectionMode) {
if (isSelectionMode) { if (isSelectionMode) {
// Используем нативный InputMethodManager для НАДЁЖНОГО закрытия клавиатуры android.util.Log.d("ChatDetailScreen", "⚠️ Backup keyboard hide triggered")
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager // Backup закрытие клавиатуры (основное в onLongClick)
imm.hideSoftInputFromWindow(view.windowToken, 0)
keyboardController?.hide() keyboardController?.hide()
focusManager.clearFocus()
showEmojiPicker = false
} }
} }
@@ -542,22 +549,22 @@ fun ChatDetailScreen(
Scaffold( Scaffold(
contentWindowInsets = WindowInsets(0.dp), contentWindowInsets = WindowInsets(0.dp),
topBar = { topBar = {
// 🔥 SELECTION HEADER (появляется при выборе сообщений) - Telegram Style // 🔥 UNIFIED HEADER - один контейнер, контент меняется внутри
AnimatedVisibility( Box(
visible = isSelectionMode, modifier = Modifier
enter = fadeIn(animationSpec = tween(200)) + slideInVertically(
initialOffsetY = { -it },
animationSpec = tween(250, easing = TelegramEasing)
),
exit = fadeOut(animationSpec = tween(150)) + slideOutVertically(
targetOffsetY = { -it },
animationSpec = tween(200, easing = TelegramEasing)
)
) {
Box(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.background(if (isDarkTheme) Color(0xFF212121) else Color.White) .background(if (isSelectionMode) {
if (isDarkTheme) Color(0xFF212121) else Color.White
} else headerBackground)
) { ) {
// Контент хедера с Crossfade для плавной смены
Crossfade(
targetState = isSelectionMode,
animationSpec = tween(200),
label = "headerContent"
) { selectionMode ->
if (selectionMode) {
// SELECTION MODE CONTENT
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -567,7 +574,7 @@ fun ChatDetailScreen(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {
// Left: X (cancel) + Count - Telegram Style // Left: X (cancel) + Count
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = { selectedMessages = emptySet() }) { IconButton(onClick = { selectedMessages = emptySet() }) {
Icon( Icon(
@@ -586,7 +593,7 @@ fun ChatDetailScreen(
) )
} }
// Right: Action buttons - Telegram Style // Right: Action buttons
Row( Row(
horizontalArrangement = Arrangement.spacedBy(4.dp), horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
@@ -617,7 +624,6 @@ fun ChatDetailScreen(
// Delete button // Delete button
IconButton( IconButton(
onClick = { onClick = {
// Удаляем выбранные сообщения
messages messages
.filter { selectedMessages.contains("${it.id}_${it.timestamp.time}") } .filter { selectedMessages.contains("${it.id}_${it.timestamp.time}") }
.forEach { msg -> viewModel.deleteMessage(msg.id) } .forEach { msg -> viewModel.deleteMessage(msg.id) }
@@ -633,38 +639,17 @@ fun ChatDetailScreen(
} }
} }
} }
} else {
// Bottom line // NORMAL HEADER CONTENT
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.height(0.5.dp)
.background(
if (isDarkTheme) Color.White.copy(alpha = 0.15f)
else Color.Black.copy(alpha = 0.1f)
)
)
}
}
// NORMAL HEADER (скрывается при выборе)
AnimatedVisibility(
visible = !isSelectionMode,
enter = fadeIn(animationSpec = tween(200)),
exit = fadeOut(animationSpec = tween(150))
) {
// Telegram-style TopAppBar - solid background без blur
Box(modifier = Modifier.fillMaxWidth().background(headerBackground)) {
Row( Row(
modifier = modifier = Modifier
Modifier.fillMaxWidth() .fillMaxWidth()
.statusBarsPadding() .statusBarsPadding()
.height(56.dp) .height(56.dp)
.padding(horizontal = 4.dp), .padding(horizontal = 4.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
// 🔔 Кнопка назад с badge непрочитанных сообщений // Back button with badge
Box { Box {
IconButton( IconButton(
onClick = hideKeyboardAndBack, onClick = hideKeyboardAndBack,
@@ -677,7 +662,6 @@ fun ChatDetailScreen(
modifier = Modifier.size(32.dp) modifier = Modifier.size(32.dp)
) )
} }
// Badge с количеством непрочитанных из других чатов
if (totalUnreadFromOthers > 0) { if (totalUnreadFromOthers > 0) {
Box( Box(
modifier = Modifier modifier = Modifier
@@ -685,12 +669,11 @@ fun ChatDetailScreen(
.offset(x = (-4).dp, y = 6.dp) .offset(x = (-4).dp, y = 6.dp)
.size(if (totalUnreadFromOthers > 9) 20.dp else 18.dp) .size(if (totalUnreadFromOthers > 9) 20.dp else 18.dp)
.clip(CircleShape) .clip(CircleShape)
.background(Color(0xFFFF3B30)), // Красный цвет как в iOS .background(Color(0xFFFF3B30)),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Text( Text(
text = if (totalUnreadFromOthers > 99) "99+" text = if (totalUnreadFromOthers > 99) "99+"
else if (totalUnreadFromOthers > 9) "$totalUnreadFromOthers"
else "$totalUnreadFromOthers", else "$totalUnreadFromOthers",
color = Color.White, color = Color.White,
fontSize = if (totalUnreadFromOthers > 9) 9.sp else 10.sp, fontSize = if (totalUnreadFromOthers > 9) 9.sp else 10.sp,
@@ -962,20 +945,21 @@ fun ChatDetailScreen(
} }
} }
} }
// Нижняя линия для разделения }
} // Закрытие Crossfade
// Bottom line для unified header
Box( Box(
modifier = modifier = Modifier
Modifier.align(Alignment.BottomCenter) .align(Alignment.BottomCenter)
.fillMaxWidth() .fillMaxWidth()
.height(0.5.dp) .height(0.5.dp)
.background( .background(
if (isDarkTheme) if (isDarkTheme) Color.White.copy(alpha = 0.15f)
Color.White.copy(alpha = 0.1f) else Color.Black.copy(alpha = 0.1f)
else Color.Black.copy(alpha = 0.08f)
) )
) )
} } // Закрытие Box unified header
} // Закрытие AnimatedVisibility для normal header
}, },
containerColor = backgroundColor, // Фон всего чата containerColor = backgroundColor, // Фон всего чата
// 🔥 Bottom bar - инпут с умным padding: // 🔥 Bottom bar - инпут с умным padding:
@@ -984,76 +968,38 @@ fun ChatDetailScreen(
bottomBar = { bottomBar = {
// 🔥 Telegram-style: когда Box с эмодзи виден, НЕ используем imePadding // 🔥 Telegram-style: когда Box с эмодзи виден, НЕ используем imePadding
// isEmojiBoxVisible учитывает анимацию fade-out (alpha > 0.01) // isEmojiBoxVisible учитывает анимацию fade-out (alpha > 0.01)
val bottomModifier = if (coordinator.isEmojiBoxVisible) { // 🔥 В selection mode НЕ используем imePadding (клавиатура закрыта)
Modifier // Без imePadding - Box с эмодзи заменяет клавиатуру val useImePadding = !coordinator.isEmojiBoxVisible && !isSelectionMode
} else { val bottomModifier = if (useImePadding) {
Modifier.imePadding() // С imePadding - клавиатура поднимает инпут Modifier.imePadding() // С imePadding - клавиатура поднимает инпут
} } else {
Column(modifier = bottomModifier) { Modifier // Без imePadding
// 🔥 FLOATING INPUT BAR - плавает поверх сообщений, поднимается с клавиатурой
// Скрываем когда в режиме выбора
AnimatedVisibility(
visible = !isSelectionMode,
enter = fadeIn(tween(200)) + slideInVertically(initialOffsetY = { it }),
exit = fadeOut(tween(150)) + slideOutVertically(targetOffsetY = { it })
) {
Column {
// Input bar с встроенным reply preview (как в React Native)
MessageInputBar(
value = inputText,
onValueChange = {
viewModel.updateInputText(it)
// Отправляем индикатор печатания
if (it.isNotEmpty() && !isSavedMessages) {
viewModel.sendTypingIndicator()
}
},
onSend = {
// Скрываем кнопку scroll на время отправки
isSendingMessage = true
viewModel.sendMessage()
// Скроллим к новому сообщению
scope.launch {
delay(100)
listState.animateScrollToItem(0)
delay(300) // Ждём завершения анимации
isSendingMessage = false
}
},
isDarkTheme = isDarkTheme,
backgroundColor = backgroundColor, // Тот же цвет что и фон чата
textColor = textColor,
placeholderColor = secondaryTextColor,
secondaryTextColor = secondaryTextColor,
// Reply state
replyMessages = replyMessages,
isForwardMode = isForwardMode,
onCloseReply = { viewModel.clearReplyMessages() },
chatTitle = chatTitle,
isBlocked = isBlocked,
// Emoji picker state (поднят для KeyboardAvoidingView)
showEmojiPicker = showEmojiPicker,
onToggleEmojiPicker = { showEmojiPicker = it },
// Focus requester для автофокуса при reply
focusRequester = inputFocusRequester,
// Coordinator для плавных переходов
coordinator = coordinator,
// 🔥 Для отображения reply preview и скролла
displayReplyMessages = displayReplyMessages,
onReplyClick = scrollToMessage
)
}
} }
// 🔥 SELECTION ACTION BAR - Reply/Forward (появляется при выборе сообщений) // Логирование состояния
// Плоский стиль как у инпута с border сверху LaunchedEffect(isSelectionMode, useImePadding, coordinator.isEmojiBoxVisible, coordinator.keyboardHeight) {
AnimatedVisibility( android.util.Log.d("ChatDetailScreen", "═══════════════════════════════════════")
visible = isSelectionMode, android.util.Log.d("ChatDetailScreen", "🔄 BOTTOM BAR STATE CHANGED")
enter = fadeIn(tween(200)) + slideInVertically(initialOffsetY = { it }), android.util.Log.d("ChatDetailScreen", " 📊 isSelectionMode: $isSelectionMode")
exit = fadeOut(tween(150)) + slideOutVertically(targetOffsetY = { it }) android.util.Log.d("ChatDetailScreen", " 📊 useImePadding: $useImePadding")
) { android.util.Log.d("ChatDetailScreen", " 📊 isEmojiBoxVisible: ${coordinator.isEmojiBoxVisible}")
Column { android.util.Log.d("ChatDetailScreen", " 📊 keyboardHeight: ${coordinator.keyboardHeight}")
// Плоский контейнер как у инпута android.util.Log.d("ChatDetailScreen", " 📊 emojiHeight: ${coordinator.emojiHeight}")
android.util.Log.d("ChatDetailScreen", "═══════════════════════════════════════")
}
Column(modifier = bottomModifier) {
// 🔥 UNIFIED BOTTOM BAR - один контейнер, контент меняется внутри
Crossfade(
targetState = isSelectionMode,
animationSpec = tween(200),
label = "bottomBarContent"
) { selectionMode ->
android.util.Log.d("ChatDetailScreen", "🎬 Crossfade to selectionMode=$selectionMode")
if (selectionMode) {
// SELECTION ACTION BAR - Reply/Forward
// 🔥 Высота должна совпадать с MessageInputBar (~56dp content + nav bar)
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -1067,12 +1013,12 @@ fun ChatDetailScreen(
.background(if (isDarkTheme) Color.White.copy(alpha = 0.15f) else Color.Black.copy(alpha = 0.1f)) .background(if (isDarkTheme) Color.White.copy(alpha = 0.15f) else Color.Black.copy(alpha = 0.1f))
) )
// Кнопки Reply и Forward // Кнопки Reply и Forward - такие же отступы как у input row
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 16.dp) .padding(horizontal = 12.dp, vertical = 8.dp)
.padding(bottom = 12.dp) .padding(bottom = 16.dp)
.navigationBarsPadding(), .navigationBarsPadding(),
horizontalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
@@ -1081,6 +1027,7 @@ fun ChatDetailScreen(
Box( Box(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.height(40.dp)
.clip(RoundedCornerShape(12.dp)) .clip(RoundedCornerShape(12.dp))
.background(PrimaryBlue.copy(alpha = 0.1f)) .background(PrimaryBlue.copy(alpha = 0.1f))
.clickable { .clickable {
@@ -1089,8 +1036,7 @@ fun ChatDetailScreen(
.sortedBy { it.timestamp } .sortedBy { it.timestamp }
viewModel.setReplyMessages(selectedMsgs) viewModel.setReplyMessages(selectedMsgs)
selectedMessages = emptySet() selectedMessages = emptySet()
} },
.padding(vertical = 16.dp),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Row( Row(
@@ -1117,6 +1063,7 @@ fun ChatDetailScreen(
Box( Box(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.height(40.dp)
.clip(RoundedCornerShape(12.dp)) .clip(RoundedCornerShape(12.dp))
.background(PrimaryBlue.copy(alpha = 0.1f)) .background(PrimaryBlue.copy(alpha = 0.1f))
.clickable { .clickable {
@@ -1125,8 +1072,7 @@ fun ChatDetailScreen(
.sortedBy { it.timestamp } .sortedBy { it.timestamp }
viewModel.setForwardMessages(selectedMsgs) viewModel.setForwardMessages(selectedMsgs)
selectedMessages = emptySet() selectedMessages = emptySet()
} },
.padding(vertical = 16.dp),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Row( Row(
@@ -1150,6 +1096,45 @@ fun ChatDetailScreen(
} }
} }
} }
} else {
// INPUT BAR
Column {
MessageInputBar(
value = inputText,
onValueChange = {
viewModel.updateInputText(it)
if (it.isNotEmpty() && !isSavedMessages) {
viewModel.sendTypingIndicator()
}
},
onSend = {
isSendingMessage = true
viewModel.sendMessage()
scope.launch {
delay(100)
listState.animateScrollToItem(0)
delay(300)
isSendingMessage = false
}
},
isDarkTheme = isDarkTheme,
backgroundColor = backgroundColor,
textColor = textColor,
placeholderColor = secondaryTextColor,
secondaryTextColor = secondaryTextColor,
replyMessages = replyMessages,
isForwardMode = isForwardMode,
onCloseReply = { viewModel.clearReplyMessages() },
chatTitle = chatTitle,
isBlocked = isBlocked,
showEmojiPicker = showEmojiPicker,
onToggleEmojiPicker = { showEmojiPicker = it },
focusRequester = inputFocusRequester,
coordinator = coordinator,
displayReplyMessages = displayReplyMessages,
onReplyClick = scrollToMessage
)
}
} }
} }
} // Закрытие Column с imePadding } // Закрытие Column с imePadding
@@ -1288,12 +1273,29 @@ fun ChatDetailScreen(
isSelected = selectedMessages.contains(selectionKey), isSelected = selectedMessages.contains(selectionKey),
isHighlighted = highlightedMessageId == message.id, isHighlighted = highlightedMessageId == message.id,
onLongClick = { 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 // Toggle selection on long press
selectedMessages = if (selectedMessages.contains(selectionKey)) { selectedMessages = if (selectedMessages.contains(selectionKey)) {
selectedMessages - selectionKey selectedMessages - selectionKey
} else { } else {
selectedMessages + selectionKey selectedMessages + selectionKey
} }
android.util.Log.d("ChatDetailScreen", " 📊 selectedMessages.size AFTER: ${selectedMessages.size}")
android.util.Log.d("ChatDetailScreen", "═══════════════════════════════════════")
}, },
onClick = { onClick = {
// If in selection mode, toggle selection // If in selection mode, toggle selection
@@ -2332,11 +2334,14 @@ private fun MessageInputBar(
.background(if (isDarkTheme) Color.White.copy(alpha = 0.1f) else Color.Black.copy(alpha = 0.08f)) .background(if (isDarkTheme) Color.White.copy(alpha = 0.1f) else Color.Black.copy(alpha = 0.08f))
) )
// BLOCKED CHAT FOOTER - плоский стиль
// 🔥 Высота должна совпадать с MessageInputBar и Selection Action Bar
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 16.dp) .padding(horizontal = 12.dp, vertical = 8.dp)
.padding(bottom = 16.dp), .padding(bottom = 16.dp)
.navigationBarsPadding(),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
) { ) {
@@ -2453,11 +2458,14 @@ private fun MessageInputBar(
} }
// INPUT ROW - Paperclip → TextField → Emoji → Send/Mic (ПЛОСКИЙ ДИЗАЙН) // INPUT ROW - Paperclip → TextField → Emoji → Send/Mic (ПЛОСКИЙ ДИЗАЙН)
// 🔥 Высота должна совпадать с Selection Action Bar (padding + nav bar)
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.heightIn(min = 48.dp) .heightIn(min = 48.dp)
.padding(horizontal = 12.dp, vertical = 8.dp), .padding(horizontal = 12.dp, vertical = 8.dp)
.padding(bottom = 16.dp) // 🔥 Такой же отступ как у Selection Action Bar
.navigationBarsPadding(), // 🔥 Такой же navigationBarsPadding
verticalAlignment = Alignment.Bottom verticalAlignment = Alignment.Bottom
) { ) {
// PAPERCLIP BUTTON (слева) // PAPERCLIP BUTTON (слева)

View File

@@ -768,9 +768,12 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
* 🔥 Очистить reply/forward * 🔥 Очистить reply/forward
*/ */
fun clearReplyMessages() { fun clearReplyMessages() {
viewModelScope.launch {
delay(350) // Задержка после закрытия панели (анимация fadeOut + shrinkVertically)
_replyMessages.value = emptyList() _replyMessages.value = emptyList()
_isForwardMode.value = false _isForwardMode.value = false
} }
}
/** /**
* 🔥 Удалить сообщение (для ошибки отправки) * 🔥 Удалить сообщение (для ошибки отправки)

View File

@@ -22,6 +22,7 @@ import androidx.compose.ui.unit.dp
* ``` * ```
*/ */
object KeyboardHeightProvider { object KeyboardHeightProvider {
private const val TAG = "KeyboardHeight"
private const val PREFS_NAME = "emoji_keyboard_prefs" private const val PREFS_NAME = "emoji_keyboard_prefs"
private const val KEY_KEYBOARD_HEIGHT = "kbd_height" private const val KEY_KEYBOARD_HEIGHT = "kbd_height"
private const val DEFAULT_HEIGHT_DP = 280 // Telegram uses 200, we use 280 private const val DEFAULT_HEIGHT_DP = 280 // Telegram uses 200, we use 280
@@ -35,7 +36,11 @@ object KeyboardHeightProvider {
val savedPx = prefs.getInt(KEY_KEYBOARD_HEIGHT, defaultPx) val savedPx = prefs.getInt(KEY_KEYBOARD_HEIGHT, defaultPx)
val isDefault = savedPx == defaultPx val isDefault = savedPx == defaultPx
android.util.Log.d("KeyboardHeight", "📖 getSavedKeyboardHeight: ${savedPx}px (${pxToDp(context, savedPx)}dp) ${if (isDefault) "[DEFAULT]" else "[SAVED]"}") 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 return savedPx
} }
@@ -43,6 +48,9 @@ object KeyboardHeightProvider {
* Сохранить высоту клавиатуры * Сохранить высоту клавиатуры
*/ */
fun saveKeyboardHeight(context: Context, heightPx: Int) { fun saveKeyboardHeight(context: Context, heightPx: Int) {
android.util.Log.d(TAG, "═══════════════════════════════════════")
android.util.Log.d(TAG, "💾 saveKeyboardHeight($heightPx px)")
if (heightPx > 0) { if (heightPx > 0) {
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
val oldHeight = prefs.getInt(KEY_KEYBOARD_HEIGHT, -1) val oldHeight = prefs.getInt(KEY_KEYBOARD_HEIGHT, -1)
@@ -50,21 +58,29 @@ object KeyboardHeightProvider {
prefs.edit().putInt(KEY_KEYBOARD_HEIGHT, heightPx).apply() 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) { if (changed) {
android.util.Log.d("KeyboardHeight", "💾 SAVED keyboard height: ${heightPx}px (${pxToDp(context, heightPx)}dp) [WAS: ${oldHeight}px]") android.util.Log.d(TAG, " SAVED successfully!")
} else { } else {
android.util.Log.v("KeyboardHeight", "💾 Same keyboard height: ${heightPx}px (no change)") android.util.Log.d(TAG, " ⏭️ Same height, no change")
} }
} else { } else {
android.util.Log.w("KeyboardHeight", "⚠️ Attempted to save invalid height: ${heightPx}px") android.util.Log.w(TAG, " ⚠️ INVALID height: ${heightPx}px - NOT saved!")
} }
android.util.Log.d(TAG, "═══════════════════════════════════════")
} }
/** /**
* Получить длительность анимации клавиатуры * Получить длительность анимации клавиатуры
* Telegram использует: AdjustPanLayoutHelper.keyboardDuration (250ms обычно) * Telegram использует: AdjustPanLayoutHelper.keyboardDuration (250ms обычно)
*/ */
fun getKeyboardAnimationDuration(): Long = 250L fun getKeyboardAnimationDuration(): Long {
android.util.Log.d(TAG, "⏱️ getKeyboardAnimationDuration() = 250ms")
return 250L
}
// Helper functions // Helper functions
private fun dpToPx(context: Context, dp: Int): Int { private fun dpToPx(context: Context, dp: Int): Int {
@@ -91,7 +107,9 @@ fun rememberSavedKeyboardHeight(): Dp {
val density = context.resources.displayMetrics.density val density = context.resources.displayMetrics.density
val heightDp = (heightPx / density).dp val heightDp = (heightPx / density).dp
android.util.Log.d("KeyboardHeight", "🎯 rememberSavedKeyboardHeight: ${heightDp} (${heightPx}px, density=${density})") android.util.Log.d("KeyboardHeight", "🎯 rememberSavedKeyboardHeight()")
android.util.Log.d("KeyboardHeight", " 📏 Result: $heightDp (${heightPx}px)")
android.util.Log.d("KeyboardHeight", " 📱 Density: $density")
return heightDp return heightDp
} }