diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Resources.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Resources.android.kt index 226e97d4d..e15d1f926 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Resources.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Resources.android.kt @@ -39,14 +39,14 @@ private val sharedPreferencesThemes: SharedPreferences by lazy { androidAppConte actual val settings: Settings by lazy { SharedPreferencesSettings(sharedPreferences) } actual val settingsThemes: Settings by lazy { SharedPreferencesSettings(sharedPreferencesThemes) } -actual fun screenOrientation(): ScreenOrientation = when (mainActivity.get()?.resources?.configuration?.orientation) { - Configuration.ORIENTATION_PORTRAIT -> ScreenOrientation.PORTRAIT - Configuration.ORIENTATION_LANDSCAPE -> ScreenOrientation.LANDSCAPE - else -> ScreenOrientation.UNDEFINED +actual fun windowOrientation(): WindowOrientation = when (mainActivity.get()?.resources?.configuration?.orientation) { + Configuration.ORIENTATION_PORTRAIT -> WindowOrientation.PORTRAIT + Configuration.ORIENTATION_LANDSCAPE -> WindowOrientation.LANDSCAPE + else -> WindowOrientation.UNDEFINED } @Composable -actual fun screenWidth(): Dp = LocalConfiguration.current.screenWidthDp.dp +actual fun windowWidth(): Dp = LocalConfiguration.current.screenWidthDp.dp actual fun desktopExpandWindowToWidth(width: Dp) {} diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt index dda00fc43..cb386be7a 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt @@ -311,7 +311,6 @@ fun DesktopScreen(settingsState: SettingsViewState) { scope.launch { if (scaffoldState.drawerState.isOpen) scaffoldState.drawerState.close() else scaffoldState.drawerState.open() } } ModalManager.fullscreen.showInView() - ModalManager.fullscreen.showPasscodeInView() } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Resources.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Resources.kt index c7799e592..2ee668fb2 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Resources.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Resources.kt @@ -19,14 +19,14 @@ expect fun isInNightMode(): Boolean expect val settings: Settings expect val settingsThemes: Settings -enum class ScreenOrientation { +enum class WindowOrientation { UNDEFINED, PORTRAIT, LANDSCAPE } -expect fun screenOrientation(): ScreenOrientation +expect fun windowOrientation(): WindowOrientation @Composable -expect fun screenWidth(): Dp +expect fun windowWidth(): Dp expect fun desktopExpandWindowToWidth(width: Dp) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt index 139b23e81..8c7dc2c60 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt @@ -93,7 +93,7 @@ fun UserPicker( } } val xOffset = with(LocalDensity.current) { 10.dp.roundToPx() } - val maxWidth = with(LocalDensity.current) { screenWidth() * density } + val maxWidth = with(LocalDensity.current) { windowWidth() * density } Box(Modifier .fillMaxSize() .offset { IntOffset(if (newChat.isGone()) -maxWidth.value.roundToInt() else xOffset, 0) } @@ -201,7 +201,7 @@ fun UserProfilePickerItem(u: User, unreadCount: Int = 0, padding: PaddingValues fun UserProfileRow(u: User) { Row( Modifier - .widthIn(max = screenWidth() * 0.7f) + .widthIn(max = windowWidth() * 0.7f) .padding(vertical = 8.dp), verticalAlignment = Alignment.CenterVertically ) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/AlertManager.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/AlertManager.kt index 49a7ad748..f5fbd0150 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/AlertManager.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/AlertManager.kt @@ -8,6 +8,7 @@ import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.* import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextAlign @@ -159,13 +160,22 @@ class AlertManager { title = alertTitle(title), text = alertText(text), buttons = { + val focusRequester = remember { FocusRequester() } + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } Row( Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING).padding(bottom = DEFAULT_PADDING_HALF), horizontalArrangement = Arrangement.Center ) { - TextButton(onClick = { - hideAlert() - }) { Text(confirmText, color = Color.Unspecified) } + TextButton( + onClick = { + hideAlert() + }, + Modifier.focusRequester(focusRequester) + ) { + Text(confirmText, color = Color.Unspecified) + } } }, shape = RoundedCornerShape(corner = CornerSize(25.dp)) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Section.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Section.kt index e59653cef..6adbfed76 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Section.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Section.kt @@ -12,7 +12,7 @@ import dev.icerock.moko.resources.compose.painterResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.* -import chat.simplex.common.platform.screenWidth +import chat.simplex.common.platform.windowWidth import chat.simplex.common.ui.theme.* import chat.simplex.common.views.helpers.* import chat.simplex.common.views.usersettings.SettingsActionItemWithContent @@ -238,7 +238,7 @@ fun InfoRow(title: String, value: String, icon: Painter? = null, iconTint: Color @Composable fun InfoRowEllipsis(title: String, value: String, onClick: () -> Unit) { SectionItemViewSpaceBetween(onClick) { - val screenWidthDp = screenWidth() + val screenWidthDp = windowWidth() Text(title) Text( value, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt index c07459a3d..a6746da69 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt @@ -306,10 +306,10 @@ fun IntSize.Companion.Saver(): Saver = Saver( fun DisposableEffectOnGone(always: () -> Unit = {}, whenDispose: () -> Unit = {}, whenGone: () -> Unit) { DisposableEffect(Unit) { always() - val orientation = screenOrientation() + val orientation = windowOrientation() onDispose { whenDispose() - if (orientation == screenOrientation()) { + if (orientation == windowOrientation()) { whenGone() } } @@ -320,10 +320,10 @@ fun DisposableEffectOnGone(always: () -> Unit = {}, whenDispose: () -> Unit = {} fun DisposableEffectOnRotate(always: () -> Unit = {}, whenDispose: () -> Unit = {}, whenRotate: () -> Unit) { DisposableEffect(Unit) { always() - val orientation = screenOrientation() + val orientation = windowOrientation() onDispose { whenDispose() - if (orientation != screenOrientation()) { + if (orientation != windowOrientation()) { whenRotate() } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/PasscodeView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/PasscodeView.kt index 3e35bfb80..4784951ad 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/PasscodeView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/PasscodeView.kt @@ -6,10 +6,11 @@ import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.* +import androidx.compose.ui.input.key.* import dev.icerock.moko.resources.compose.painterResource import androidx.compose.ui.unit.dp -import chat.simplex.common.platform.ScreenOrientation -import chat.simplex.common.platform.screenOrientation +import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.DEFAULT_PADDING import chat.simplex.common.views.helpers.SimpleButton import chat.simplex.common.views.helpers.* @@ -25,9 +26,43 @@ fun PasscodeView( submit: () -> Unit, cancel: () -> Unit, ) { + val focusRequester = remember { FocusRequester() } + + @Composable + fun Modifier.handleKeyboard(): Modifier { + val numbers = remember { + arrayOf( + Key.Zero, Key.One, Key.Two, Key.Three, Key.Four, Key.Five, Key.Six, Key.Seven, Key.Eight, Key.Nine, + Key.NumPad0, Key.NumPad1, Key.NumPad2, Key.NumPad3, Key.NumPad4, Key.NumPad5, Key.NumPad6, Key.NumPad7, Key.NumPad8, Key.NumPad9 + ) + } + return onPreviewKeyEvent { + if (it.key in numbers && it.type == KeyEventType.KeyDown) { + if (passcode.value.length < 16) { + passcode.value += numbers.indexOf(it.key) % 10 + } + true + } else if (it.key == Key.Backspace && it.type == KeyEventType.KeyDown && (it.isCtrlPressed || it.isMetaPressed)) { + passcode.value = "" + true + } else if (it.key == Key.Backspace && it.type == KeyEventType.KeyDown) { + passcode.value = passcode.value.dropLast(1) + true + } else if ((it.key == Key.Enter || it.key == Key.NumPadEnter) && it.type == KeyEventType.KeyUp) { + if ((submitEnabled?.invoke(passcode.value) != false && passcode.value.length >= 4)) { + submit() + } + true + } else { + false + } + } + } + @Composable fun VerticalLayout() { Column( + Modifier.handleKeyboard().focusRequester(focusRequester), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.SpaceEvenly ) { @@ -38,7 +73,7 @@ fun PasscodeView( } } PasscodeEntry(passcode, true) - Row { + Row(Modifier.heightIn(min = 70.dp), verticalAlignment = Alignment.CenterVertically) { SimpleButton(generalGetString(MR.strings.cancel_verb), icon = painterResource(MR.images.ic_close), click = cancel) Spacer(Modifier.size(20.dp)) SimpleButton(submitLabel, icon = painterResource(MR.images.ic_done_filled), disabled = submitEnabled?.invoke(passcode.value) == false || passcode.value.length < 4, click = submit) @@ -48,9 +83,9 @@ fun PasscodeView( @Composable fun HorizontalLayout() { - Row(Modifier.padding(horizontal = DEFAULT_PADDING), horizontalArrangement = Arrangement.Center) { + Row(Modifier.padding(horizontal = DEFAULT_PADDING).handleKeyboard().focusRequester(focusRequester), horizontalArrangement = Arrangement.Center) { Column( - Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, top = DEFAULT_PADDING * 4), + Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, top = DEFAULT_PADDING), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.SpaceBetween ) { @@ -64,7 +99,7 @@ fun PasscodeView( } Column( - Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, top = DEFAULT_PADDING * 4), + Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, top = DEFAULT_PADDING), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.SpaceBetween ) { @@ -90,9 +125,14 @@ fun PasscodeView( } } - if (screenOrientation() == ScreenOrientation.PORTRAIT) { + if (windowOrientation() == WindowOrientation.PORTRAIT || appPlatform.isDesktop) { VerticalLayout() } else { HorizontalLayout() } + LaunchedEffect(Unit) { + focusRequester.requestFocus() + // Disallow to steal a focus by clicking on buttons or using Tab + focusRequester.captureFocus() + } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/PasswordEntry.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/PasswordEntry.kt index ad7c26deb..f76b82c31 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/PasswordEntry.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/PasswordEntry.kt @@ -1,7 +1,6 @@ package chat.simplex.common.views.localauth -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable +import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* @@ -16,6 +15,7 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.* +import chat.simplex.common.platform.appPlatform import chat.simplex.res.MR @Composable @@ -39,7 +39,7 @@ fun PasscodeEntry( fun PasscodeView(password: MutableState) { var showPasscode by rememberSaveable { mutableStateOf(false) } Text( - if (password.value.isEmpty()) " " else remember(password.value, showPasscode) { splitPassword(showPasscode, password.value) }, + if (password.value.isEmpty()) "" else remember(password.value, showPasscode) { splitPassword(showPasscode, password.value) }, Modifier.padding(vertical = 10.dp).clickable { showPasscode = !showPasscode }, style = MaterialTheme.typography.body1 ) @@ -47,7 +47,7 @@ fun PasscodeView(password: MutableState) { @Composable private fun BoxWithConstraintsScope.VerticalPasswordGrid(password: MutableState) { - val s = minOf(maxWidth, maxHeight) / 4 - 1.dp + val s = if (appPlatform.isAndroid) minOf(maxWidth, maxHeight) / 4 - 1.dp else minOf(minOf(maxWidth, maxHeight) / 4 - 1.dp, 100.dp) Column(Modifier.width(IntrinsicSize.Min)) { DigitsRow(s, 1, 2, 3, password) Divider() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt index 171465d86..8ec54e344 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt @@ -97,7 +97,7 @@ private fun NewChatSheetLayout( } } val endPadding = if (appPlatform.isDesktop) 56.dp else 0.dp - val maxWidth = with(LocalDensity.current) { screenWidth() * density } + val maxWidth = with(LocalDensity.current) { windowWidth() * density } Column( Modifier .fillMaxSize() diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Resources.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Resources.desktop.kt index 5aeddd299..b75898822 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Resources.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Resources.desktop.kt @@ -36,18 +36,15 @@ private val settingsThemesProps = actual val settings: Settings = PropertiesSettings(settingsProps) { settingsProps.store(settingsFile.writer(), "") } actual val settingsThemes: Settings = PropertiesSettings(settingsThemesProps) { settingsThemesProps.store(settingsThemesFile.writer(), "") } -actual fun screenOrientation(): ScreenOrientation = ScreenOrientation.UNDEFINED - -@Composable // LALAL -actual fun screenWidth(): Dp { - return java.awt.Toolkit.getDefaultToolkit().screenSize.width.dp - /*var width by remember { mutableStateOf(java.awt.Toolkit.getDefaultToolkit().screenSize.width.also { println("LALAL $it") }) } - SideEffect { - if (width != java.awt.Toolkit.getDefaultToolkit().screenSize.width) - width = java.awt.Toolkit.getDefaultToolkit().screenSize.width +actual fun windowOrientation(): WindowOrientation = + if (simplexWindowState.windowState.size.width > simplexWindowState.windowState.size.height) { + WindowOrientation.LANDSCAPE + } else { + WindowOrientation.PORTRAIT } - return width*/ -}// LALAL java.awt.Desktop.getDesktop() + +@Composable +actual fun windowWidth(): Dp = simplexWindowState.windowState.size.width actual fun desktopExpandWindowToWidth(width: Dp) { if (simplexWindowState.windowState.size.width >= width) return