feat: Implement keyboard height provider and optimize emoji picker animations
This commit is contained in:
@@ -2011,17 +2011,39 @@ private fun MessageInputBar(
|
||||
val imeHeight = with(density) { imeInsets.getBottom(density).toDp() }
|
||||
val isKeyboardVisible = imeHeight > 50.dp // 🔥 Согласованный порог с ChatDetailScreen
|
||||
|
||||
// 🔥 Запоминаем высоту клавиатуры когда она открыта
|
||||
// Дефолт 320.dp - хорошая высота для большинства устройств
|
||||
var savedKeyboardHeight by remember { mutableStateOf(320.dp) }
|
||||
// 🔥 Флаг "клавиатура в процессе анимации"
|
||||
var isKeyboardAnimating by remember { mutableStateOf(false) }
|
||||
|
||||
// 🔥 Логирование изменений высоты клавиатуры
|
||||
LaunchedEffect(imeHeight) {
|
||||
if (imeHeight > 100.dp) {
|
||||
savedKeyboardHeight = imeHeight
|
||||
}
|
||||
android.util.Log.d("KeyboardHeight", "📊 IME height: $imeHeight (visible=$isKeyboardVisible, showEmojiPicker=$showEmojiPicker, animating=$isKeyboardAnimating)")
|
||||
}
|
||||
|
||||
// Высота панели эмодзи = сохранённая высота клавиатуры (минимум 280.dp)
|
||||
val emojiPanelHeight = maxOf(savedKeyboardHeight, 280.dp)
|
||||
// 🔥 Запоминаем высоту клавиатуры когда она открыта (Telegram-style with SharedPreferences)
|
||||
LaunchedEffect(Unit) {
|
||||
// Загружаем сохранённую высоту при старте
|
||||
val savedHeightPx = com.rosetta.messenger.ui.components.KeyboardHeightProvider.getSavedKeyboardHeight(context)
|
||||
android.util.Log.d("KeyboardHeight", "📱 MessageInputBar initialized, loaded height: ${savedHeightPx}px")
|
||||
}
|
||||
|
||||
// 🔥 Сохраняем высоту ТОЛЬКО когда клавиатура стабильна
|
||||
// Используем отдельный LaunchedEffect который НЕ реагирует на каждое изменение
|
||||
LaunchedEffect(isKeyboardVisible, showEmojiPicker) {
|
||||
// Если клавиатура стала видимой и emoji закрыт
|
||||
if (isKeyboardVisible && !showEmojiPicker && !isKeyboardAnimating) {
|
||||
// Ждем стабилизации
|
||||
isKeyboardAnimating = true
|
||||
kotlinx.coroutines.delay(300) // Анимация клавиатуры ~250ms
|
||||
isKeyboardAnimating = false
|
||||
|
||||
// Сохраняем только если всё еще видна и emoji закрыт
|
||||
if (isKeyboardVisible && !showEmojiPicker && imeHeight > 300.dp) {
|
||||
val heightPx = with(density) { imeHeight.toPx().toInt() }
|
||||
com.rosetta.messenger.ui.components.KeyboardHeightProvider.saveKeyboardHeight(context, heightPx)
|
||||
android.util.Log.d("KeyboardHeight", "✅ Stable keyboard height saved: ${imeHeight} (${heightPx}px)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Состояние отправки - можно отправить если есть текст ИЛИ есть reply
|
||||
val canSend = remember(value, hasReply) { value.isNotBlank() || hasReply }
|
||||
@@ -2049,14 +2071,19 @@ private fun MessageInputBar(
|
||||
fun toggleEmojiPicker() {
|
||||
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
|
||||
android.util.Log.d("EmojiPicker", "🔥 toggleEmojiPicker called, showEmojiPicker=$showEmojiPicker")
|
||||
android.util.Log.d("EmojiPicker", "=".repeat(60))
|
||||
android.util.Log.d("EmojiPicker", "🔥 toggleEmojiPicker START")
|
||||
android.util.Log.d("EmojiPicker", " State: showEmojiPicker=$showEmojiPicker, isKeyboardVisible=$isKeyboardVisible")
|
||||
android.util.Log.d("EmojiPicker", " IME height: $imeHeight, editTextView=${if (editTextView != null) "SET" else "NULL"}")
|
||||
|
||||
if (showEmojiPicker) {
|
||||
// ========== ЗАКРЫВАЕМ EMOJI → ОТКРЫВАЕМ КЛАВИАТУРУ ==========
|
||||
android.util.Log.d("EmojiPicker", "📱 Closing emoji, opening keyboard")
|
||||
android.util.Log.d("EmojiPicker", "📱 Action: CLOSING emoji → OPENING keyboard")
|
||||
val startTime = System.currentTimeMillis()
|
||||
|
||||
// Шаг 1: Закрываем emoji панель
|
||||
onToggleEmojiPicker(false)
|
||||
android.util.Log.d("EmojiPicker", " [1] Emoji panel closed")
|
||||
|
||||
// Шаг 2: Немедленно фокусируем и открываем клавиатуру
|
||||
editTextView?.let { editText ->
|
||||
@@ -2064,44 +2091,51 @@ private fun MessageInputBar(
|
||||
|
||||
// Метод 1: Немедленный вызов
|
||||
imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED)
|
||||
android.util.Log.d("EmojiPicker", "✅ Called showSoftInput FORCED (immediate)")
|
||||
android.util.Log.d("EmojiPicker", " [2] Method 1: showSoftInput(FORCED) called")
|
||||
|
||||
// Метод 2: Через post (следующий frame)
|
||||
view.post {
|
||||
val elapsed = System.currentTimeMillis() - startTime
|
||||
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT)
|
||||
android.util.Log.d("EmojiPicker", "✅ Called showSoftInput IMPLICIT (post)")
|
||||
android.util.Log.d("EmojiPicker", " [3] Method 2: showSoftInput(IMPLICIT) called (${elapsed}ms)")
|
||||
}
|
||||
|
||||
// Метод 3: Через postDelayed (100ms)
|
||||
view.postDelayed({
|
||||
val elapsed = System.currentTimeMillis() - startTime
|
||||
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)
|
||||
android.util.Log.d("EmojiPicker", "✅ Called toggleSoftInput (100ms)")
|
||||
android.util.Log.d("EmojiPicker", " [4] Method 3: toggleSoftInput called (${elapsed}ms)")
|
||||
}, 100)
|
||||
|
||||
// Метод 4: Финальная попытка через 200ms
|
||||
view.postDelayed({
|
||||
val elapsed = System.currentTimeMillis() - startTime
|
||||
if (!isKeyboardVisible) {
|
||||
android.util.Log.d("EmojiPicker", "⚠️ Keyboard still not visible, forcing again")
|
||||
android.util.Log.w("EmojiPicker", " [5] ⚠️ Keyboard still not visible after ${elapsed}ms, forcing again")
|
||||
imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED)
|
||||
} else {
|
||||
android.util.Log.d("EmojiPicker", "✅ Keyboard is visible!")
|
||||
android.util.Log.d("EmojiPicker", " [5] ✅ Keyboard opened successfully in ${elapsed}ms!")
|
||||
}
|
||||
}, 200)
|
||||
} ?: android.util.Log.e("EmojiPicker", "❌ editTextView is null!")
|
||||
} ?: android.util.Log.e("EmojiPicker", " ❌ ERROR: editTextView is null!")
|
||||
} else {
|
||||
// ========== ОТКРЫВАЕМ EMOJI → ЗАКРЫВАЕМ КЛАВИАТУРУ ==========
|
||||
android.util.Log.d("EmojiPicker", "😊 Opening emoji, closing keyboard")
|
||||
android.util.Log.d("EmojiPicker", "😊 Action: OPENING emoji → CLOSING keyboard")
|
||||
|
||||
// Шаг 1: Скрываем клавиатуру
|
||||
imm.hideSoftInputFromWindow(view.windowToken, 0)
|
||||
android.util.Log.d("EmojiPicker", " [1] Keyboard hide requested")
|
||||
|
||||
// Шаг 2: Небольшая задержка для плавности
|
||||
view.postDelayed({
|
||||
// Шаг 3: Открываем emoji панель
|
||||
onToggleEmojiPicker(true)
|
||||
android.util.Log.d("EmojiPicker", "✅ Emoji panel opened")
|
||||
android.util.Log.d("EmojiPicker", " [2] ✅ Emoji panel opened (50ms delay)")
|
||||
}, 50)
|
||||
}
|
||||
|
||||
android.util.Log.d("EmojiPicker", "🔥 toggleEmojiPicker END")
|
||||
android.util.Log.d("EmojiPicker", "=".repeat(60))
|
||||
}
|
||||
|
||||
// Функция отправки - НЕ закрывает клавиатуру (UX правило #6)
|
||||
|
||||
Reference in New Issue
Block a user