diff --git a/app/src/main/java/app/rosette/android/ui/keyboard/KeyboardTransitionCoordinator.kt b/app/src/main/java/app/rosette/android/ui/keyboard/KeyboardTransitionCoordinator.kt index 865ed12..a0a987f 100644 --- a/app/src/main/java/app/rosette/android/ui/keyboard/KeyboardTransitionCoordinator.kt +++ b/app/src/main/java/app/rosette/android/ui/keyboard/KeyboardTransitionCoordinator.kt @@ -54,7 +54,7 @@ class KeyboardTransitionCoordinator { var keyboardHeight by mutableStateOf(0.dp) var emojiHeight by mutableStateOf(0.dp) - // 🔥 Сохраняем максимальную высоту клавиатуры для правильного восстановления emoji + // Максимальная высота клавиатуры (защищает от промежуточных значений при анимации закрытия) private var maxKeyboardHeight by mutableStateOf(0.dp) // ============ Флаги видимости ============ @@ -84,8 +84,10 @@ class KeyboardTransitionCoordinator { currentState = TransitionState.KEYBOARD_TO_EMOJI isTransitioning = true - // 🔥 Гарантируем что emojiHeight = maxKeyboardHeight (не меняется при закрытии клавиатуры) - if (maxKeyboardHeight > 0.dp) { + // Устанавливаем emojiHeight = текущая высота клавиатуры (клавиатура ещё открыта) + if (keyboardHeight > 0.dp) { + emojiHeight = keyboardHeight + } else if (maxKeyboardHeight > 0.dp) { emojiHeight = maxKeyboardHeight } @@ -234,7 +236,7 @@ class KeyboardTransitionCoordinator { if (height > 100.dp && height != keyboardHeight) { keyboardHeight = height - // 🔥 Сохраняем максимальную высоту + // Обновляем maxKeyboardHeight только вверх (защита от промежуточных значений анимации) if (height > maxKeyboardHeight) { maxKeyboardHeight = height } @@ -244,14 +246,14 @@ class KeyboardTransitionCoordinator { emojiHeight = height } } else if (height == 0.dp && keyboardHeight != 0.dp) { - // 🔥 Клавиатура закрывается - восстанавливаем emojiHeight до МАКСИМАЛЬНОЙ высоты - - // Восстанавливаем emojiHeight до максимальной высоты - if (maxKeyboardHeight > 0.dp) { + // Клавиатура закрывается. + // Если emoji уже показан (keyboard→emoji переход), НЕ трогаем emojiHeight — + // requestShowEmoji() уже установил правильное значение = текущая высота клавиатуры. + // Восстанавливаем из maxKeyboardHeight только если emoji НЕ виден (обычное закрытие). + if (!isEmojiVisible && !isEmojiBoxVisible && maxKeyboardHeight > 0.dp) { emojiHeight = maxKeyboardHeight } - // Обнуляем keyboardHeight keyboardHeight = 0.dp } @@ -272,7 +274,8 @@ class KeyboardTransitionCoordinator { * emojiHeight должна оставаться фиксированной! */ fun syncHeights() { - // 🔥 Синхронизируем ТОЛЬКО если клавиатура ОТКРЫТА и высота больше текущей emoji + // Синхронизируем только вверх — при закрытии клавиатуры промежуточные значения + // не должны уменьшать emojiHeight. Точная высота ставится в requestShowEmoji(). if (keyboardHeight > 100.dp && keyboardHeight > emojiHeight) { emojiHeight = keyboardHeight } diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/GroupSetupScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/GroupSetupScreen.kt index bf953a1..a9db9bd 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/GroupSetupScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/GroupSetupScreen.kt @@ -7,6 +7,8 @@ import android.view.inputmethod.InputMethodManager import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -243,15 +245,28 @@ fun GroupSetupScreen( } var lastStableKeyboardHeight by remember { mutableStateOf(0.dp) } + var isKeyboardVisible by remember { mutableStateOf(false) } LaunchedEffect(imeBottomPx) { val currentImeHeight = with(density) { imeBottomPx.toDp() } coordinator.updateKeyboardHeight(currentImeHeight) + isKeyboardVisible = currentImeHeight > 50.dp if (currentImeHeight > 100.dp) { coordinator.syncHeights() lastStableKeyboardHeight = currentImeHeight } } + // Save keyboard height for emoji picker sync + LaunchedEffect(isKeyboardVisible, showEmojiKeyboard) { + if (isKeyboardVisible && !showEmojiKeyboard) { + kotlinx.coroutines.delay(350) + if (isKeyboardVisible && !showEmojiKeyboard && lastStableKeyboardHeight > 300.dp) { + val heightPx = with(density) { lastStableKeyboardHeight.toPx().toInt() } + KeyboardHeightProvider.saveKeyboardHeight(context, heightPx) + } + } + } + fun toggleEmojiPicker() { val now = System.currentTimeMillis() if (now - lastToggleTime < toggleCooldownMs || step != GroupSetupStep.DETAILS || isLoading) return @@ -725,22 +740,24 @@ fun GroupSetupScreen( containerColor = if (actionEnabled) accentColor else accentColor.copy(alpha = 0.42f), contentColor = Color.White, shape = CircleShape, - modifier = + modifier = run { + var lastStableFabBottom by remember { mutableStateOf(18.dp) } + val rawBottom = if (imeBottomDp > 0.dp) { + imeBottomDp + 14.dp + } else if (coordinator.isEmojiBoxVisible && coordinator.emojiHeight > 0.dp) { + coordinator.emojiHeight + 14.dp + } else { + 18.dp + } + // During keyboard switch, keep FAB at last stable position + if (!coordinator.isTransitioning) { + lastStableFabBottom = rawBottom + } Modifier .align(Alignment.BottomEnd) - .padding( - end = 16.dp, - // Consistent: always 14dp above whichever keyboard is showing. - // This Box ignores paddingValues, so we measure from raw screen bottom. - bottom = if (imeBottomDp > 0.dp) { - imeBottomDp + 14.dp - } else if (coordinator.isEmojiBoxVisible && coordinator.emojiHeight > 0.dp) { - coordinator.emojiHeight + 14.dp - } else { - 18.dp - } - ) + .padding(end = 16.dp, bottom = lastStableFabBottom) .size(58.dp) + } ) { if (isLoading && step == GroupSetupStep.DESCRIPTION) { CircularProgressIndicator(