Fix for message composer losing focus in Compose 1.8.0 (#4853)

Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
Jorge Martin Espinosa 2025-06-10 18:39:26 +02:00 committed by GitHub
parent d2e2080489
commit cfb9cc3edc
12 changed files with 46 additions and 25 deletions

View file

@ -10,7 +10,10 @@
package io.element.android.features.messages.impl package io.element.android.features.messages.impl
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SheetValue import androidx.compose.material3.SheetValue
import androidx.compose.material3.rememberBottomSheetScaffoldState import androidx.compose.material3.rememberBottomSheetScaffoldState
@ -112,7 +115,7 @@ internal fun ExpandableBottomSheetScaffold(
} }
SubcomposeLayout( SubcomposeLayout(
modifier = modifier, modifier = modifier.windowInsetsPadding(WindowInsets.ime),
measurePolicy = { constraints: Constraints -> measurePolicy = { constraints: Constraints ->
val sheetContentSub = subcompose(Slot.SheetContent(sheetContentKey)) { sheetContent(true) }.map { val sheetContentSub = subcompose(Slot.SheetContent(sheetContentKey)) { sheetContent(true) }.map {
it.measure(Constraints(maxWidth = constraints.maxWidth)) it.measure(Constraints(maxWidth = constraints.maxWidth))
@ -123,7 +126,7 @@ internal fun ExpandableBottomSheetScaffold(
val dragHandleHeight = dragHandleSub?.height?.toDp() ?: 0.dp val dragHandleHeight = dragHandleSub?.height?.toDp() ?: 0.dp
val maxHeight = constraints.maxHeight.toDp() val maxHeight = constraints.maxHeight.toDp()
val contentHeight = sheetContentSub.height.toDp() + dragHandleHeight val contentHeight = sheetContentSub.measuredHeight.toDp() + dragHandleHeight
contentOverflows = contentHeight > maxHeight contentOverflows = contentHeight > maxHeight
@ -140,7 +143,7 @@ internal fun ExpandableBottomSheetScaffold(
measurePolicy = { measurables, constraints -> measurePolicy = { measurables, constraints ->
val constraintHeight = constraints.maxHeight val constraintHeight = constraints.maxHeight
val offset = tryOrNull { scaffoldState.bottomSheetState.requireOffset() } ?: 0f val offset = tryOrNull { scaffoldState.bottomSheetState.requireOffset() } ?: 0f
val height = Integer.max(0, constraintHeight - offset.roundToInt()) val height = Integer.max(peekHeight.roundToPx(), constraintHeight - offset.roundToInt())
val top = measurables[0].measure( val top = measurables[0].measure(
constraints.copy( constraints.copy(
minHeight = height, minHeight = height,

View file

@ -27,6 +27,11 @@ fun View.showKeyboard(andRequestFocus: Boolean = false) {
imm?.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT) imm?.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT)
} }
fun View.isKeyboardVisible(): Boolean {
val imm = context?.getSystemService<InputMethodManager>()
return imm?.isAcceptingText == true
}
suspend fun View.awaitWindowFocus() = suspendCancellableCoroutine { continuation -> suspend fun View.awaitWindowFocus() = suspendCancellableCoroutine { continuation ->
if (hasWindowFocus()) { if (hasWindowFocus()) {
continuation.resume(Unit) continuation.resume(Unit)

View file

@ -7,6 +7,8 @@
package io.element.android.libraries.textcomposer package io.element.android.libraries.textcomposer
import android.os.Build
import android.view.WindowInsets
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -14,6 +16,7 @@ import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import io.element.android.libraries.androidutils.ui.awaitWindowFocus import io.element.android.libraries.androidutils.ui.awaitWindowFocus
import io.element.android.libraries.androidutils.ui.isKeyboardVisible
import io.element.android.libraries.androidutils.ui.showKeyboard import io.element.android.libraries.androidutils.ui.showKeyboard
/** /**
@ -40,11 +43,17 @@ internal fun <T> SoftKeyboardEffect(
// Await window focus in case returning from a dialog // Await window focus in case returning from a dialog
view.awaitWindowFocus() view.awaitWindowFocus()
// Show the keyboard, temporarily using the root view for focus if (!view.isKeyboardVisible()) {
view.showKeyboard(andRequestFocus = true) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
view.windowInsetsController?.show(WindowInsets.Type.ime())
} else {
// Show the keyboard, temporarily using the root view for focus
view.showKeyboard(andRequestFocus = true)
}
// Refocus to the correct view // Refocus to the correct view
latestOnRequestFocus() latestOnRequestFocus()
}
} }
} }
} }

View file

@ -18,12 +18,14 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeightIn import androidx.compose.foundation.layout.requiredHeightIn
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -35,6 +37,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
@ -432,8 +435,9 @@ private fun TextFormattingLayout(
sendButton: @Composable () -> Unit, sendButton: @Composable () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val bottomPadding = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() + 8.dp }
Column( Column(
modifier = modifier.padding(vertical = 4.dp), modifier = modifier.padding(vertical = 4.dp).padding(bottom = bottomPadding),
verticalArrangement = Arrangement.spacedBy(4.dp), verticalArrangement = Arrangement.spacedBy(4.dp),
) { ) {
if (isRoomEncrypted == false) { if (isRoomEncrypted == false) {

View file

@ -8,7 +8,6 @@
package io.element.android.libraries.textcomposer.components.markdown package io.element.android.libraries.textcomposer.components.markdown
import android.content.Context import android.content.Context
import android.view.View
import androidx.appcompat.widget.AppCompatEditText import androidx.appcompat.widget.AppCompatEditText
internal class MarkdownEditText( internal class MarkdownEditText(
@ -37,8 +36,4 @@ internal class MarkdownEditText(
onSelectionChangeListener?.invoke(selStart, selEnd) onSelectionChangeListener?.invoke(selStart, selEnd)
} }
} }
override fun focusSearch(direction: Int): View? {
return null
}
} }

View file

@ -118,6 +118,11 @@ fun MarkdownTextInput(
) )
} }
state.requestFocusAction = { this.requestFocus() } state.requestFocusAction = { this.requestFocus() }
} else {
isEnabled = false
isFocusable = false
isFocusableInTouchMode = false
isClickable = false
} }
} }
}, },

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:390f7f3c699ecd843eb79adcefc64a66e6bbb6740dcd3bccf0f90c2f2b70e7e2 oid sha256:70ab6c6c738a96fd2db7901482cf4110dfcc69a6289e1db734a753751690747a
size 54255 size 51622

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c6465d7f2e552193d51dd1701d6b4f5726cb616e35cdcd3e219ebf31c22941b9 oid sha256:237ee8f45cfd01821287669abc4fe7125c0d2828ff60224706eef855910821f0
size 53750 size 51048

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c86b3656e5913338c2513af8a56dc725a63d4fda9c5478f429ec684203c68115 oid sha256:17f126215961643b6b7c3b41fcc5ea970923229b76bb7c918db191cb5c0ef8b9
size 64550 size 64672

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1b491d682990192c54ebdae58045caab1ff653d3d6c89ca5f15e93bbb45c6fc0 oid sha256:f72fdf44bd35b6d62b5e75ec1bf2206429e0861da684d370259bea684eb2a76e
size 62052 size 62158

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d2bc5c13d2a1e100bdcd0142b7f03b458ba80d0d6ded82de1ebb1b6aa055a344 oid sha256:d5226d7599af16015858523607a829d9cbbc0ffa21174f9c528e725673e2e31c
size 53893 size 53982

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:fb2210aa87440b2e87217ff53448091078bdff658a6b36fad12cb1a57caf59e2 oid sha256:31b2d698e78fa11dc6af9dbc20b07533afd16c26b8f9969756057f07ea4785d0
size 51187 size 51371