diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt index 3fcf31124c..c58c460888 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt @@ -31,6 +31,13 @@ class MainActivity: FragmentActivity() { val c = CurrentColors.value.colors platform.androidSetStatusAndNavigationBarAppearance(c.isLight) applyAppLocale(ChatModel.controller.appPrefs.appLanguage) + // This flag makes status bar and navigation bar fully transparent. But on API level < 30 it breaks insets entirely + // https://issuetracker.google.com/issues/236862874 + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q){ + window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS) + } else { + window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) + } super.onCreate(savedInstanceState) // testJson() // When call ended and orientation changes, it re-process old intent, it's unneeded. diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexApp.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexApp.kt index cb39b97a84..e5eefd7a2e 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexApp.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexApp.kt @@ -7,6 +7,8 @@ import chat.simplex.common.platform.Log import android.content.Intent import android.content.pm.ActivityInfo import android.os.* +import android.view.View +import android.view.WindowManager.LayoutParams import androidx.compose.animation.core.* import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -285,6 +287,13 @@ class SimplexApp: Application(), LifecycleEventObserver { if (windowInsetController?.isAppearanceLightNavigationBars != CurrentColors.value.colors.isLight) { windowInsetController?.isAppearanceLightNavigationBars = CurrentColors.value.colors.isLight } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + window.decorView.systemUiVisibility = if (CurrentColors.value.colors.isLight) { + View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR + } else { + 0 + } + } } override fun androidStartCallActivity(acceptCall: Boolean, remoteHostId: Long?, chatId: ChatId?) { diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/ScrollableColumn.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/ScrollableColumn.android.kt index db2c2d708f..e1673962a8 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/ScrollableColumn.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/ScrollableColumn.android.kt @@ -8,8 +8,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalLayoutDirection -import chat.simplex.common.model.ChatController.appPrefs +import chat.simplex.common.views.chatlist.NavigationBarBackground import chat.simplex.common.views.helpers.* import kotlinx.coroutines.flow.filter import kotlin.math.absoluteValue @@ -39,14 +38,12 @@ actual fun LazyColumnWithScrollBar( } } } - val direction = LocalLayoutDirection.current - val paddingEnd = WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(direction) if (connection != null) { - LazyColumn(modifier.nestedScroll(connection).padding(end = paddingEnd), state, contentPadding, reverseLayout, verticalArrangement, horizontalAlignment, flingBehavior, userScrollEnabled) { + LazyColumn(modifier.nestedScroll(connection), state, contentPadding, reverseLayout, verticalArrangement, horizontalAlignment, flingBehavior, userScrollEnabled) { content() } } else { - LazyColumn(modifier.padding(end = paddingEnd), state, contentPadding, reverseLayout, verticalArrangement, horizontalAlignment, flingBehavior, userScrollEnabled) { + LazyColumn(modifier, state, contentPadding, reverseLayout, verticalArrangement, horizontalAlignment, flingBehavior, userScrollEnabled) { content() } } @@ -74,25 +71,30 @@ actual fun ColumnWithScrollBar( } } } - val direction = LocalLayoutDirection.current - if (connection != null) { + Box(Modifier.fillMaxHeight()) { + if (connection != null) { Column( if (maxIntrinsicSize) { - modifier.nestedScroll(connection).verticalScroll(state).height(IntrinsicSize.Max).padding(end = WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(direction)) + modifier.nestedScroll(connection).verticalScroll(state).height(IntrinsicSize.Max) } else { - modifier.nestedScroll(connection).verticalScroll(state).padding(end = WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(direction)) - }, verticalArrangement, horizontalAlignment) { + modifier.nestedScroll(connection).verticalScroll(state) + }, verticalArrangement, horizontalAlignment + ) { content() Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars)) } - } else { - Column(if (maxIntrinsicSize) { - modifier.verticalScroll(state).height(IntrinsicSize.Max).padding(end = WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(direction)) - } else { - modifier.verticalScroll(state).padding(end = WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(direction)) - }, verticalArrangement, horizontalAlignment) { + } else { + Column( + if (maxIntrinsicSize) { + modifier.verticalScroll(state).height(IntrinsicSize.Max) + } else { + modifier.verticalScroll(state) + }, verticalArrangement, horizontalAlignment + ) { content() Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars)) } + } + NavigationBarBackground() } } diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/UI.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/UI.android.kt index c90946c95b..7ab6bf525f 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/UI.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/UI.android.kt @@ -3,17 +3,18 @@ package chat.simplex.common.platform import android.app.Activity import android.content.Context import android.content.pm.ActivityInfo -import android.graphics.Rect import android.os.* import android.view.* import android.view.inputmethod.InputMethodManager import android.widget.Toast import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.ime import androidx.compose.runtime.* +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalView import chat.simplex.common.AppScreen import chat.simplex.common.model.clear -import chat.simplex.common.ui.theme.SimpleXTheme import chat.simplex.common.views.helpers.* import androidx.compose.ui.platform.LocalContext as LocalContext1 import chat.simplex.res.MR @@ -43,28 +44,13 @@ actual fun LocalMultiplatformView(): Any? = LocalView.current @Composable actual fun getKeyboardState(): State { - val keyboardState = remember { mutableStateOf(KeyboardState.Closed) } - val view = LocalView.current - DisposableEffect(view) { - val onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener { - val rect = Rect() - view.getWindowVisibleDisplayFrame(rect) - val screenHeight = view.rootView.height - val keypadHeight = screenHeight - rect.bottom - keyboardState.value = if (keypadHeight > screenHeight * 0.15) { - KeyboardState.Opened - } else { - KeyboardState.Closed - } - } - view.viewTreeObserver.addOnGlobalLayoutListener(onGlobalListener) - - onDispose { - view.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalListener) + val density = LocalDensity.current + val ime = WindowInsets.ime + return remember { + derivedStateOf { + if (ime.getBottom(density) == 0) KeyboardState.Closed else KeyboardState.Opened } } - - return keyboardState } actual fun hideKeyboard(view: Any?, clearFocus: Boolean) { diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.android.kt index 2b24e7e6c5..24f2f62620 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.android.kt @@ -138,65 +138,73 @@ actual fun PlatformUserPicker(modifier: Modifier, pickerState: MutableStateFlow< } else { Modifier } - Box( - Modifier - .fillMaxSize() - .then(clickableModifier) - .drawBehind { - val pos = when { - dismissState.progress.from == DismissValue.Default && dismissState.progress.to == DismissValue.Default -> 1f - dismissState.progress.from == DismissValue.DismissedToEnd && dismissState.progress.to == DismissValue.DismissedToEnd -> 0f - dismissState.progress.to == DismissValue.Default -> dismissState.progress.fraction - else -> 1 - dismissState.progress.fraction - } - val colors = CurrentColors.value.colors - val resultingColor = if (colors.isLight) colors.onSurface.copy(alpha = ScrimOpacity) else Color.Black.copy(0.64f) - drawRect( - if (pos != 0f) resultingColor else Color.Transparent, - alpha = calculateFraction(pos = pos) - ) - } - .graphicsLayer { - if (heightValue == 0) { - alpha = 0f - } - translationY = dismissState.offset.value - }, - contentAlignment = Alignment.BottomCenter - ) { + Box { Box( - Modifier.onSizeChanged { height.intValue = it.height } - ) { - KeyChangeEffect(pickerIsVisible) { - if (pickerState.value.isVisible()) { - try { - dismissState.animateTo(DismissValue.Default, userPickerAnimSpec()) - } catch (e: CancellationException) { - Log.e(TAG, "Cancelled animateTo: ${e.stackTraceToString()}") - pickerState.value = AnimatedViewState.GONE + Modifier + .fillMaxSize() + .then(clickableModifier) + .drawBehind { + val pos = calculatePosition(dismissState) + val colors = CurrentColors.value.colors + val resultingColor = if (colors.isLight) colors.onSurface.copy(alpha = ScrimOpacity) else Color.Black.copy(0.64f) + drawRect( + if (pos != 0f) resultingColor else Color.Transparent, + alpha = calculateFraction(pos = pos) + ) + } + .graphicsLayer { + if (heightValue == 0) { + alpha = 0f } - } else { - try { - dismissState.animateTo(DismissValue.DismissedToEnd, userPickerAnimSpec()) - } catch (e: CancellationException) { - Log.e(TAG, "Cancelled animateTo2: ${e.stackTraceToString()}") - pickerState.value = AnimatedViewState.VISIBLE + translationY = dismissState.offset.value + }, + contentAlignment = Alignment.BottomCenter + ) { + Box( + Modifier.onSizeChanged { height.intValue = it.height } + ) { + KeyChangeEffect(pickerIsVisible) { + if (pickerState.value.isVisible()) { + try { + dismissState.animateTo(DismissValue.Default, userPickerAnimSpec()) + } catch (e: CancellationException) { + Log.e(TAG, "Cancelled animateTo: ${e.stackTraceToString()}") + pickerState.value = AnimatedViewState.GONE + } + } else { + try { + dismissState.animateTo(DismissValue.DismissedToEnd, userPickerAnimSpec()) + } catch (e: CancellationException) { + Log.e(TAG, "Cancelled animateTo2: ${e.stackTraceToString()}") + pickerState.value = AnimatedViewState.VISIBLE + } } } - } - val draggableModifier = if (height.intValue != 0) - Modifier.draggableBottomDrawerModifier( - state = dismissState, - swipeDistance = height.intValue.toFloat(), - ) - else Modifier - Box(draggableModifier.then(modifier)) { - content() + val draggableModifier = if (height.intValue != 0) + Modifier.draggableBottomDrawerModifier( + state = dismissState, + swipeDistance = height.intValue.toFloat(), + ) + else Modifier + Box(draggableModifier.then(modifier).navigationBarsPadding()) { + content() + } } } + NavigationBarBackground( + modifier = Modifier.graphicsLayer { alpha = if (calculatePosition(dismissState) > 0.1f) 1f else 0f }, + color = MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, alpha = 1 - userPickerAlpha()) + ) } } +private fun calculatePosition(dismissState: DismissState): Float = when { + dismissState.progress.from == DismissValue.Default && dismissState.progress.to == DismissValue.Default -> 1f + dismissState.progress.from == DismissValue.DismissedToEnd && dismissState.progress.to == DismissValue.DismissedToEnd -> 0f + dismissState.progress.to == DismissValue.Default -> dismissState.progress.fraction + else -> 1 - dismissState.progress.fraction +} + private fun Modifier.draggableBottomDrawerModifier( state: DismissState, swipeDistance: Float, diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/GetImageView.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/GetImageView.android.kt index 98d1f8fb19..1c7ba1dcf0 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/GetImageView.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/GetImageView.android.kt @@ -171,6 +171,8 @@ actual fun GetImageBottomSheet( modifier = Modifier .fillMaxWidth() .wrapContentHeight() + .imePadding() + .navigationBarsPadding() .onFocusChanged { focusState -> if (!focusState.hasFocus) hideBottomSheet() } 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 aeda8b4043..6709af8e33 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 @@ -12,9 +12,9 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clipToBounds -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.dp import chat.simplex.common.views.usersettings.SetDeliveryReceiptsView import chat.simplex.common.model.* @@ -45,7 +45,22 @@ fun AppScreen() { AppBarHandler.appBarMaxHeightPx = with(LocalDensity.current) { AppBarHeight.roundToPx() } SimpleXTheme { Surface(color = MaterialTheme.colors.background, contentColor = LocalContentColor.current) { - MainScreen() + // This padding applies to landscape view only taking care of navigation bar and holes in screen in status bar area + // (because nav bar and holes located on vertical sides of screen in landscape view) + val direction = LocalLayoutDirection.current + val safePadding = WindowInsets.safeDrawing.asPaddingValues() + val paddingStart = safePadding.calculateStartPadding(direction) + val paddingEnd = safePadding.calculateEndPadding(direction) + // Such a strange layout is needed because the main content should be covered by solid color in order to hide overflow + // of some elements that may have negative offset (so, can't use Row {}). + // To check: go to developer settings of Android, choose Display cutout -> Punch hole, and rotate the phone to landscape, open any chat + Box { + Box(Modifier.padding(start = paddingStart, end = paddingEnd).consumeWindowInsets(PaddingValues(start = paddingStart, end = paddingEnd))) { + MainScreen() + } + Box(Modifier.width(paddingStart).fillMaxHeight().background(MaterialTheme.colors.background)) + Box(Modifier.align(Alignment.CenterEnd).width(paddingEnd).fillMaxHeight().background(MaterialTheme.colors.background)) + } } } } @@ -245,7 +260,10 @@ fun AndroidScreen(userPickerState: MutableStateFlow) { Box( Modifier .graphicsLayer { - translationX = -offset.value.dp.toPx() + // minOf thing is needed for devices with holes in screen while the user on ChatView rotates his phone from portrait to landscape + // because in this case (at least in emulator) maxWidth changes in two steps: big first, smaller on next frame. + // But offset is remembered already, so this is a better way than dropping a value of offset + translationX = -minOf(offset.value.dp, maxWidth).toPx() } .padding(top = if (showCallArea) ANDROID_CALL_TOP_PADDING else 0.dp) ) { @@ -275,7 +293,7 @@ fun AndroidScreen(userPickerState: MutableStateFlow) { } } Box(Modifier - .graphicsLayer { translationX = maxWidth.toPx() - offset.value.dp.toPx() } + .graphicsLayer { translationX = maxWidth.toPx() - minOf(offset.value.dp, maxWidth).toPx() } .padding(top = if (showCallArea) ANDROID_CALL_TOP_PADDING else 0.dp) ) Box2@{ currentChatId.value?.let { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/TerminalView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/TerminalView.kt index 2741025bc5..83d2198ba3 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/TerminalView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/TerminalView.kt @@ -20,6 +20,7 @@ import chat.simplex.common.views.chat.* import chat.simplex.common.views.helpers.* import chat.simplex.common.model.ChatModel import chat.simplex.common.platform.* +import chat.simplex.common.views.chatlist.NavigationBarBackground import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @@ -170,13 +171,16 @@ fun TerminalLog(floating: Boolean) { ModalManager.start } modalPlace.showModal(endButtons = { ShareButton { clipboard.shareText(item.details) } }) { - SelectionContainer(modifier = Modifier.verticalScroll(rememberScrollState()).navigationBarsPadding()) { - val details = item.details - .let { - if (it.length < 100_000) it - else it.substring(0, 100_000) - } - Text(details, modifier = Modifier.heightIn(max = 50_000.dp).padding(horizontal = DEFAULT_PADDING).padding(bottom = DEFAULT_PADDING)) + Box(Modifier.fillMaxHeight()) { + SelectionContainer(modifier = Modifier.verticalScroll(rememberScrollState()).navigationBarsPadding()) { + val details = item.details + .let { + if (it.length < 100_000) it + else it.substring(0, 100_000) + } + Text(details, modifier = Modifier.heightIn(max = 50_000.dp).padding(horizontal = DEFAULT_PADDING).padding(bottom = DEFAULT_PADDING)) + } + NavigationBarBackground() } } }.padding(horizontal = 8.dp, vertical = 4.dp) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index ad59832388..1098d7c267 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -704,7 +704,7 @@ fun ChatLayout( } @Composable -fun ChatInfoToolbar( +fun ColumnScope.ChatInfoToolbar( chatInfo: ChatInfo, back: () -> Unit, info: () -> Unit, @@ -866,12 +866,12 @@ fun ChatInfoToolbar( onSearchValueChanged = onSearchValueChanged, buttons = barButtons ) - Box(Modifier.fillMaxWidth().wrapContentSize(Alignment.TopEnd)) { + Box(Modifier.align(Alignment.End)) { DefaultDropdownMenu(showMenu) { menuItems.forEach { it() } } } - Divider() + Divider(Modifier.background(MaterialTheme.colors.background)) } @Composable @@ -1360,8 +1360,8 @@ fun BoxScope.FloatingButtons( onLongClick = { showDropDown.value = true } ) - Box(Modifier.offset { IntOffset(maxWidth.value - DEFAULT_PADDING.roundToPx(), (24.dp + fabSize).roundToPx()) }) { - DefaultDropdownMenu(showDropDown) { + Box(Modifier.align(Alignment.TopEnd)) { + DefaultDropdownMenu(showDropDown, offset = DpOffset(-DEFAULT_PADDING, 24.dp + fabSize)) { ItemAction( generalGetString(MR.strings.mark_read), painterResource(MR.images.ic_check), diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt index 155d3c13c8..c7a68f5cba 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt @@ -1045,7 +1045,6 @@ fun ComposeView( ) } Spacer(Modifier.imePadding().navigationBarsPadding()) - // WindowInsets.ime.asPaddingValues().calculateBottomPadding() } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt index b5a62033ed..d9ded1a1b7 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt @@ -283,6 +283,7 @@ fun ModalData.GroupChatInfoLayout( if (s.isEmpty()) members else members.filter { m -> m.anyNameContains(s) } } } + Box { LazyColumnWithScrollBar( Modifier .fillMaxWidth(), @@ -397,9 +398,11 @@ fun ModalData.GroupChatInfoLayout( } } SectionBottomSpacer() - Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars)) + Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) } } + NavigationBarBackground() + } } @Composable diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt index ced38d0276..e632e5a13b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.blur import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.* import androidx.compose.ui.graphics.* @@ -587,13 +588,29 @@ enum class ScrollDirection { Up, Down, Idle } +private const val SYSTEM_BAR_BACKGROUND_ALPHA = 0.6f + @Composable -fun StatusBarBackground() { - Box(Modifier.background(MaterialTheme.colors.background).windowInsetsTopHeight(WindowInsets.statusBars)) +fun BoxScope.StatusBarBackground() { + if (appPlatform.isAndroid) { + val finalColor = MaterialTheme.colors.background.copy(SYSTEM_BAR_BACKGROUND_ALPHA) + Box(Modifier.fillMaxWidth().windowInsetsTopHeight(WindowInsets.statusBars).background(finalColor)) + } } @Composable -private fun ChatList(chatModel: ChatModel, searchText: MutableState) { +fun BoxScope.NavigationBarBackground(modifier: Modifier = Modifier, color: Color = MaterialTheme.colors.background) { + val keyboardState = getKeyboardState() + if (appPlatform.isAndroid && keyboardState.value == KeyboardState.Closed) { + val barPadding = WindowInsets.navigationBars.asPaddingValues() + val paddingBottom = barPadding.calculateBottomPadding() + val finalColor = color.copy(SYSTEM_BAR_BACKGROUND_ALPHA) + Box(modifier.align(Alignment.BottomStart).height(paddingBottom).fillMaxWidth().background(finalColor)) + } +} + +@Composable +private fun BoxScope.ChatList(chatModel: ChatModel, searchText: MutableState) { val listState = rememberLazyListState(lazyListState.first, lazyListState.second) var scrollDirection by remember { mutableStateOf(ScrollDirection.Idle) } var previousIndex by remember { mutableStateOf(0) } @@ -631,10 +648,6 @@ private fun ChatList(chatModel: ChatModel, searchText: MutableState(null) } val chats = filteredChats(showUnreadAndFavorites, searchShowingSimplexLink, searchChatFilteredBySimplexLink, searchText.value.text, allChats.value.toList()) - Column { - if (oneHandUI.value) { - StatusBarBackground() - } LazyColumnWithScrollBar( Modifier.fillMaxSize().then(if (!oneHandUI.value) Modifier.imePadding() else Modifier), listState, @@ -684,16 +697,20 @@ private fun ChatList(chatModel: ChatModel, searchText: MutableState {} } - Box(Modifier.padding(it)) { - Column( - modifier = Modifier.fillMaxSize() - ) { - if (oneHandUI.value) { - StatusBarBackground() - } - if (chatModel.chats.value.isNotEmpty()) { - ShareList( - chatModel, - search = searchInList, - isMediaOrFileAttachment = isMediaOrFileAttachment, - isVoice = isVoice, - hasSimplexLink = hasSimplexLink, - ) - } else { - EmptyList() - } + Box(Modifier.fillMaxSize().padding(it)) { + if (chatModel.chats.value.isNotEmpty()) { + ShareList( + chatModel, + search = searchInList, + isMediaOrFileAttachment = isMediaOrFileAttachment, + isVoice = isVoice, + hasSimplexLink = hasSimplexLink, + ) + } else { + EmptyList() + } + if (oneHandUI.value) { + StatusBarBackground() + } else { + NavigationBarBackground() } } } @@ -228,9 +226,9 @@ private fun ShareList( hasSimplexLink = hasSimplexLink, ) } - if (!oneHandUI.value) { + if (appPlatform.isAndroid) { item { - Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) + Spacer(if (oneHandUI.value) Modifier.windowInsetsTopHeight(WindowInsets.statusBars) else Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) } } } 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 c3ad135b24..4a3bce7752 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 @@ -143,7 +143,6 @@ fun UserPicker( .fillMaxWidth() .then(if (newChat.isVisible()) Modifier.shadow(8.dp, clip = true) else Modifier) .background(if (appPlatform.isAndroid) MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, alpha = 1 - userPickerAlpha()) else MaterialTheme.colors.surface) - .navigationBarsPadding() .padding(bottom = USER_PICKER_SECTION_SPACING - DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL), pickerState = userPickerState ) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/CloseSheetBar.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/CloseSheetBar.kt index a20ce2a488..43dd35b8fd 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/CloseSheetBar.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/CloseSheetBar.kt @@ -12,7 +12,6 @@ import androidx.compose.desktop.ui.tooling.preview.Preview import androidx.compose.foundation.background import androidx.compose.ui.draw.* import androidx.compose.ui.graphics.* -import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.* import chat.simplex.common.platform.appPlatform @@ -100,8 +99,7 @@ fun CloseSheetBar(close: (() -> Unit)?, showClose: Boolean = true, tintColor: Co } else { Spacer(Modifier.weight(1f)) } - val direction = LocalLayoutDirection.current - Row(Modifier.padding(end = WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(direction))) { + Row { endButtons() } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddGroupView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddGroupView.kt index b2be72f36f..95178d9a45 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddGroupView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddGroupView.kt @@ -102,7 +102,6 @@ fun AddGroupLayout( ColumnWithScrollBar( Modifier .fillMaxSize() - .padding(horizontal = DEFAULT_PADDING) ) { AppBarTitle(stringResource(MR.strings.create_secret_group_title), hostDevice(rhId)) Box( @@ -121,7 +120,7 @@ fun AddGroupLayout( } } } - Row(Modifier.padding(bottom = DEFAULT_PADDING_HALF).fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { + Row(Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING_HALF).fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Text( stringResource(MR.strings.group_display_name_field), fontSize = 16.sp @@ -133,7 +132,9 @@ fun AddGroupLayout( } } } - ProfileNameField(displayName, "", { isValidDisplayName(it.trim()) }, focusRequester) + Box(Modifier.padding(horizontal = DEFAULT_PADDING)) { + ProfileNameField(displayName, "", { isValidDisplayName(it.trim()) }, focusRequester) + } Spacer(Modifier.height(8.dp)) SettingsActionItem( 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 caccb428a7..f0d48fa67c 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 @@ -30,8 +30,7 @@ import chat.simplex.common.model.* import chat.simplex.common.model.ChatController.appPrefs import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* -import chat.simplex.common.views.chatlist.ScrollDirection -import chat.simplex.common.views.chatlist.StatusBarBackground +import chat.simplex.common.views.chatlist.* import chat.simplex.common.views.contacts.* import chat.simplex.common.views.helpers.* import chat.simplex.res.MR @@ -183,10 +182,7 @@ private fun ModalData.NewChatSheetLayout( derivedStateOf { filterContactTypes(chatModel.chats.value, deletedContactTypes) } } - Column { - if (oneHandUI.value) { - StatusBarBackground() - } + Box { LazyColumnWithScrollBar( Modifier.fillMaxSize().then(if (!oneHandUI.value) Modifier.imePadding() else Modifier), listState, @@ -350,7 +346,17 @@ private fun ModalData.NewChatSheetLayout( } ContactListNavLinkView(chat, nextChatSelected, showDeletedChatIcon = true) } + if (appPlatform.isAndroid) { + item { + Spacer(if (oneHandUI.value) Modifier.windowInsetsTopHeight(WindowInsets.statusBars) else Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) + } + } } + if (oneHandUI.value) { + StatusBarBackground() + } else { + NavigationBarBackground() + } } } @@ -588,10 +594,7 @@ private fun ModalData.DeletedContactsView(rh: RemoteHostInfo?, closeDeletedChats contactChats = allChats ) - Column { - if (oneHandUI.value) { - StatusBarBackground() - } + Box { LazyColumnWithScrollBar( Modifier.fillMaxSize().then(if (!oneHandUI.value) Modifier.imePadding() else Modifier), contentPadding = contentPadding, @@ -644,7 +647,17 @@ private fun ModalData.DeletedContactsView(rh: RemoteHostInfo?, closeDeletedChats } ContactListNavLinkView(chat, nextChatSelected, showDeletedChatIcon = false) } + if (appPlatform.isAndroid) { + item { + Spacer(if (oneHandUI.value) Modifier.windowInsetsTopHeight(WindowInsets.statusBars) else Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) + } + } } + if (oneHandUI.value) { + StatusBarBackground() + } else { + NavigationBarBackground() + } } } }