diff --git a/app/src/main/java/com/rosetta/messenger/ui/auth/ConfirmSeedPhraseScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/auth/ConfirmSeedPhraseScreen.kt index f348a82..cbd9cdf 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/auth/ConfirmSeedPhraseScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/auth/ConfirmSeedPhraseScreen.kt @@ -26,7 +26,6 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.rosetta.messenger.ui.onboarding.PrimaryBlue -import com.rosetta.messenger.ui.components.HideKeyboardOnDispose // Beautiful solid colors that fit the theme private val wordColors = listOf( @@ -51,9 +50,6 @@ fun ConfirmSeedPhraseScreen( onBack: () -> Unit, onConfirmed: () -> Unit ) { - // 🔥 Автоматическое скрытие клавиатуры при выходе с экрана - HideKeyboardOnDispose() - val backgroundColor = if (isDarkTheme) Color(0xFF1E1E1E) else Color(0xFFFFFFFF) val textColor = if (isDarkTheme) Color.White else Color.Black val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666) diff --git a/app/src/main/java/com/rosetta/messenger/ui/auth/SelectAccountScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/auth/SelectAccountScreen.kt index 3f4dd3d..0a0cccd 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/auth/SelectAccountScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/auth/SelectAccountScreen.kt @@ -22,7 +22,6 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.rosetta.messenger.ui.onboarding.PrimaryBlue -import com.rosetta.messenger.ui.components.HideKeyboardOnDispose data class AccountInfo( val id: String, @@ -64,9 +63,6 @@ fun SelectAccountScreen( onImportSeed: () -> Unit, onDismissModal: () -> Unit ) { - // 🔥 Автоматическое скрытие клавиатуры при выходе с экрана - HideKeyboardOnDispose() - val backgroundColor = if (isDarkTheme) Color(0xFF1E1E1E) else Color(0xFFFFFFFF) val textColor = if (isDarkTheme) Color.White else Color.Black val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666) diff --git a/app/src/main/java/com/rosetta/messenger/ui/auth/SetPasswordScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/auth/SetPasswordScreen.kt index fe9aa53..352d285 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/auth/SetPasswordScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/auth/SetPasswordScreen.kt @@ -31,7 +31,6 @@ import com.rosetta.messenger.data.DecryptedAccount import com.rosetta.messenger.data.EncryptedAccount import com.rosetta.messenger.network.ProtocolManager import com.rosetta.messenger.ui.onboarding.PrimaryBlue -import com.rosetta.messenger.ui.components.HideKeyboardOnDispose import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @@ -42,9 +41,6 @@ fun SetPasswordScreen( onBack: () -> Unit, onAccountCreated: (DecryptedAccount) -> Unit ) { - // 🔥 Автоматическое скрытие клавиатуры при выходе с экрана - HideKeyboardOnDispose() - val themeAnimSpec = tween(durationMillis = 500, easing = CubicBezierEasing(0.4f, 0f, 0.2f, 1f)) val backgroundColor by animateColorAsState(if (isDarkTheme) AuthBackground else AuthBackgroundLight, animationSpec = themeAnimSpec) val textColor by animateColorAsState(if (isDarkTheme) Color.White else Color.Black, animationSpec = themeAnimSpec) diff --git a/app/src/main/java/com/rosetta/messenger/ui/auth/UnlockScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/auth/UnlockScreen.kt index 75e8a16..3ccee7c 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/auth/UnlockScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/auth/UnlockScreen.kt @@ -42,7 +42,6 @@ import com.rosetta.messenger.network.ProtocolManager import com.rosetta.messenger.ui.onboarding.PrimaryBlue import com.rosetta.messenger.ui.chats.getAvatarColor import com.rosetta.messenger.ui.chats.getAvatarText -import com.rosetta.messenger.ui.components.HideKeyboardOnDispose import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -61,9 +60,6 @@ fun UnlockScreen( onUnlocked: (DecryptedAccount) -> Unit, onSwitchAccount: () -> Unit = {} ) { - // 🔥 Автоматическое скрытие клавиатуры при выходе с экрана - HideKeyboardOnDispose() - val themeAnimSpec = tween(durationMillis = 500, easing = CubicBezierEasing(0.4f, 0f, 0.2f, 1f)) val backgroundColor by animateColorAsState(if (isDarkTheme) AuthBackground else AuthBackgroundLight, animationSpec = themeAnimSpec) val textColor by animateColorAsState(if (isDarkTheme) Color.White else Color.Black, animationSpec = themeAnimSpec) 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 874c91c..dc30a50 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 @@ -67,8 +67,6 @@ import com.rosetta.messenger.network.SearchUser import com.rosetta.messenger.ui.components.AppleEmojiPickerPanel import com.rosetta.messenger.ui.components.AppleEmojiText import com.rosetta.messenger.ui.components.AppleEmojiTextField -import com.rosetta.messenger.ui.components.HideKeyboardOnDispose -import com.rosetta.messenger.ui.components.rememberKeyboardController import com.rosetta.messenger.ui.components.VerifiedBadge import com.rosetta.messenger.ui.onboarding.PrimaryBlue import android.view.inputmethod.InputMethodManager @@ -218,15 +216,11 @@ fun ChatDetailScreen( onUserProfileClick: () -> Unit = {}, viewModel: ChatViewModel = viewModel() ) { - // 🔥 Backup: скрывает клавиатуру при dispose (если не скрыли раньше) - HideKeyboardOnDispose() - val keyboardController = LocalSoftwareKeyboardController.current val focusManager = LocalFocusManager.current val clipboardManager = androidx.compose.ui.platform.LocalClipboardManager.current val context = LocalContext.current val view = LocalView.current - val keyboard = rememberKeyboardController() val database = remember { com.rosetta.messenger.database.RosettaDatabase.getDatabase(context) } // 🔔 Badge: количество непрочитанных сообщений из других чатов @@ -288,9 +282,9 @@ fun ChatDetailScreen( var selectedMessages by remember { mutableStateOf>(emptySet()) } val isSelectionMode = selectedMessages.isNotEmpty() - // 🔥 Простое закрытие + // 🔥 Закрытие экрана val hideKeyboardAndBack: () -> Unit = { - keyboard.hide() + keyboardController?.hide() onBack() } diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/SearchScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/SearchScreen.kt index f1a2426..2da2ca8 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/SearchScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/SearchScreen.kt @@ -25,7 +25,6 @@ import androidx.compose.ui.unit.sp import com.rosetta.messenger.data.RecentSearchesManager import com.rosetta.messenger.network.ProtocolState import com.rosetta.messenger.network.SearchUser -import com.rosetta.messenger.ui.components.HideKeyboardOnDispose // Primary Blue color private val PrimaryBlue = Color(0xFF54A9EB) @@ -44,9 +43,6 @@ fun SearchScreen( onBackClick: () -> Unit, onUserSelect: (SearchUser) -> Unit ) { - // 🔥 Автоматическое скрытие клавиатуры при выходе с экрана - HideKeyboardOnDispose() - // Цвета ТОЧНО как в ChatsListScreen val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF) val textColor = if (isDarkTheme) Color.White else Color(0xFF1a1a1a) diff --git a/app/src/main/java/com/rosetta/messenger/ui/components/KeyboardController.kt b/app/src/main/java/com/rosetta/messenger/ui/components/KeyboardController.kt deleted file mode 100644 index 17c68c1..0000000 --- a/app/src/main/java/com/rosetta/messenger/ui/components/KeyboardController.kt +++ /dev/null @@ -1,166 +0,0 @@ -package com.rosetta.messenger.ui.components - -import android.app.Activity -import android.content.Context -import android.view.View -import android.view.inputmethod.InputMethodManager -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.remember -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.platform.LocalView -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat - -/** - * 🎹 Контроллер клавиатуры для Rosetta Messenger - * - * Использует современный WindowInsetsController (API 30+) для мгновенного - * скрытия клавиатуры. Решает проблему "залипания" клавиатуры при навигации. - * - * Использование: - * ```kotlin - * @Composable - * fun MyScreen() { - * // Автоматически скрывает клавиатуру при выходе с экрана - * HideKeyboardOnDispose() - * - * // Или получите контроллер для ручного управления - * val keyboard = rememberKeyboardController() - * Button(onClick = { keyboard.hide() }) { Text("Hide") } - * } - * ``` - */ -class KeyboardManager( - private val view: View, - private val context: Context -) { - /** - * 🔥 Скрыть клавиатуру СИНХРОННО и МГНОВЕННО - * Использует InputMethodManager - самый надёжный способ - */ - fun hideNow() { - // 1. Убираем фокус с текущего view - view.clearFocus() - - // 2. InputMethodManager - синхронное скрытие - val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager - imm?.hideSoftInputFromWindow(view.windowToken, 0) - - // 3. WindowInsetsController как backup - try { - ViewCompat.getWindowInsetsController(view)?.hide(WindowInsetsCompat.Type.ime()) - } catch (e: Exception) { - // Ignore - } - } - - /** - * Скрыть клавиатуру (асинхронно через WindowInsetsController) - */ - fun hide() { - ViewCompat.getWindowInsetsController(view)?.hide(WindowInsetsCompat.Type.ime()) - } - - /** - * Показать клавиатуру - */ - fun show() { - ViewCompat.getWindowInsetsController(view)?.show(WindowInsetsCompat.Type.ime()) - } - - /** - * Скрыть клавиатуру с fallback для старых устройств - */ - fun hideWithFallback() { - // Сначала пробуем WindowInsetsController - ViewCompat.getWindowInsetsController(view)?.hide(WindowInsetsCompat.Type.ime()) - - // Fallback через InputMethodManager - val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager - imm?.hideSoftInputFromWindow(view.windowToken, 0) - } - - /** - * Проверить, видна ли клавиатура - */ - fun isVisible(): Boolean { - val insets = ViewCompat.getRootWindowInsets(view) - return insets?.isVisible(WindowInsetsCompat.Type.ime()) == true - } -} - -/** - * Composable для получения KeyboardManager - */ -@Composable -fun rememberKeyboardController(): KeyboardManager { - val view = LocalView.current - val context = LocalContext.current - return remember(view, context) { KeyboardManager(view, context) } -} - -/** - * 🔥 Автоматически скрывает клавиатуру при выходе с экрана (onDispose) - * - * ВАЖНО: Это backup. Основное скрытие должно быть в hideKeyboardAndBack() - * - * Добавьте в начало любого Composable экрана с клавиатурой: - * ```kotlin - * @Composable - * fun ChatScreen() { - * HideKeyboardOnDispose() - * // ... остальной UI - * } - * ``` - */ -@OptIn(ExperimentalComposeUiApi::class) -@Composable -fun HideKeyboardOnDispose() { - val keyboard = rememberKeyboardController() - - DisposableEffect(Unit) { - onDispose { - // Backup скрытие при dispose - keyboard.hideNow() - } - } -} - -/** - * 🔥 Расширенная версия с очисткой фокуса - * - * Использовать когда нужно также сбросить фокус с TextField - */ -@OptIn(ExperimentalComposeUiApi::class) -@Composable -fun HideKeyboardAndClearFocusOnDispose() { - val keyboard = rememberKeyboardController() - val focusManager = LocalFocusManager.current - - DisposableEffect(Unit) { - onDispose { - // Сбрасываем фокус - focusManager.clearFocus(force = true) - // Мгновенное скрытие - keyboard.hideNow() - } - } -} - -/** - * Extension function для View - скрыть клавиатуру мгновенно - */ -fun View.hideKeyboardNow() { - ViewCompat.getWindowInsetsController(this)?.hide(WindowInsetsCompat.Type.ime()) -} - -/** - * Extension function для View - показать клавиатуру - */ -fun View.showKeyboardNow() { - ViewCompat.getWindowInsetsController(this)?.show(WindowInsetsCompat.Type.ime()) -}