Skip to content

Commit b1f55ce

Browse files
author
Haoyu Zhang
committed
Fix: hardware keyboard append doesn't work when there is composition
Developers reported that when TextField has an active composition, inserting from hardware keyboard doesn't work correctly. Instead of appending the character after the cursor, the newly inserted character will replace the entire composing text. This conflicts with the behavior on software keyboard. Fully fix the issue needs careful design and much more work. This CL only provides a simple workaround: if the input is from hardware keyaboard, we always finalize the current composition before commiting the new change. Bug: 188102001 Test: ./gradlew test Test: manually tested within demo App Change-Id: I5aa09229c40ee780b3ca30af761355dc18791989
1 parent 22fb706 commit b1f55ce

File tree

2 files changed

+14
-13
lines changed

2 files changed

+14
-13
lines changed

compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldKeyInput.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import androidx.compose.ui.input.key.type
2929
import androidx.compose.ui.input.key.utf16CodePoint
3030
import androidx.compose.ui.text.input.CommitTextCommand
3131
import androidx.compose.ui.text.input.EditCommand
32+
import androidx.compose.ui.text.input.FinishComposingTextCommand
3233
import androidx.compose.ui.text.input.OffsetMapping
3334
import androidx.compose.ui.text.input.TextFieldValue
3435

@@ -56,7 +57,7 @@ internal class TextFieldKeyInput(
5657
private val keyMapping: KeyMapping = platformDefaultKeyMapping,
5758
) {
5859
private fun EditCommand.apply() {
59-
state.onValueChange(state.processor.apply(listOf(this)))
60+
state.onValueChange(state.processor.apply(listOf(FinishComposingTextCommand(), this)))
6061
}
6162

6263
private fun typedCommand(event: KeyEvent): CommitTextCommand? =

compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/RecordingInputConnection.android.kt

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -213,19 +213,19 @@ internal class RecordingInputConnection(
213213
}
214214

215215
override fun setSelection(start: Int, end: Int): Boolean = ensureActive {
216-
if (DEBUG) { logDebug("setSelection($start, $end") }
216+
if (DEBUG) { logDebug("setSelection($start, $end)") }
217217
addEditCommandWithBatch(SetSelectionCommand(start, end))
218218
return true
219219
}
220220

221221
override fun finishComposingText(): Boolean = ensureActive {
222-
if (DEBUG) { logDebug("finishComposingText(") }
222+
if (DEBUG) { logDebug("finishComposingText()") }
223223
addEditCommandWithBatch(FinishComposingTextCommand())
224224
return true
225225
}
226226

227227
override fun sendKeyEvent(event: KeyEvent): Boolean = ensureActive {
228-
if (DEBUG) { logDebug("sendKeyEvent($event") }
228+
if (DEBUG) { logDebug("sendKeyEvent($event)") }
229229
eventCallback.onKeyEvent(event)
230230
return true
231231
}
@@ -261,13 +261,13 @@ internal class RecordingInputConnection(
261261
}
262262

263263
override fun requestCursorUpdates(cursorUpdateMode: Int): Boolean = ensureActive {
264-
if (DEBUG) { logDebug("requestCursorUpdates($cursorUpdateMode") }
264+
if (DEBUG) { logDebug("requestCursorUpdates($cursorUpdateMode)") }
265265
Log.w(TAG, "requestCursorUpdates is not supported")
266266
return false
267267
}
268268

269269
override fun getExtractedText(request: ExtractedTextRequest?, flags: Int): ExtractedText {
270-
if (DEBUG) { logDebug("getExtractedText($request, $flags") }
270+
if (DEBUG) { logDebug("getExtractedText($request, $flags)") }
271271
extractedTextMonitorMode = (flags and InputConnection.GET_EXTRACTED_TEXT_MONITOR) != 0
272272
if (extractedTextMonitorMode) {
273273
currentExtractedTextRequestToken = request?.token ?: 0
@@ -296,13 +296,13 @@ internal class RecordingInputConnection(
296296
// /////////////////////////////////////////////////////////////////////////////////////////////
297297

298298
override fun performContextMenuAction(id: Int): Boolean = ensureActive {
299-
if (DEBUG) { logDebug("performContextMenuAction($id") }
299+
if (DEBUG) { logDebug("performContextMenuAction($id)") }
300300
Log.w(TAG, "performContextMenuAction is not supported")
301301
return false
302302
}
303303

304304
override fun performEditorAction(editorAction: Int): Boolean = ensureActive {
305-
if (DEBUG) { logDebug("performEditorAction($editorAction") }
305+
if (DEBUG) { logDebug("performEditorAction($editorAction)") }
306306
val imeAction = when (editorAction) {
307307
EditorInfo.IME_ACTION_UNSPECIFIED -> ImeAction.Default
308308
EditorInfo.IME_ACTION_DONE -> ImeAction.Done
@@ -325,7 +325,7 @@ internal class RecordingInputConnection(
325325
// /////////////////////////////////////////////////////////////////////////////////////////////
326326

327327
override fun commitCompletion(text: CompletionInfo?): Boolean = ensureActive {
328-
if (DEBUG) { logDebug("commitCompletion(${text?.text}") }
328+
if (DEBUG) { logDebug("commitCompletion(${text?.text})") }
329329
// We don't support this callback.
330330
// The API documents says this should return if the input connection is no longer valid, but
331331
// The Chromium implementation already returning false, so assuming it is safe to return
@@ -342,12 +342,12 @@ internal class RecordingInputConnection(
342342
}
343343

344344
override fun getHandler(): Handler? {
345-
if (DEBUG) { logDebug("getHandler(") }
345+
if (DEBUG) { logDebug("getHandler()") }
346346
return null // Returns null means using default Handler
347347
}
348348

349349
override fun clearMetaKeyStates(states: Int): Boolean = ensureActive {
350-
if (DEBUG) { logDebug("clearMetaKeyStates($states") }
350+
if (DEBUG) { logDebug("clearMetaKeyStates($states)") }
351351
// We don't support this callback.
352352
// The API documents says this should return if the input connection is no longer valid, but
353353
// The Chromium implementation already returning false, so assuming it is safe to return
@@ -357,12 +357,12 @@ internal class RecordingInputConnection(
357357
}
358358

359359
override fun reportFullscreenMode(enabled: Boolean): Boolean {
360-
if (DEBUG) { logDebug("reportFullscreenMode($enabled") }
360+
if (DEBUG) { logDebug("reportFullscreenMode($enabled)") }
361361
return false // This value is ignored according to the API docs.
362362
}
363363

364364
override fun getCursorCapsMode(reqModes: Int): Int {
365-
if (DEBUG) { logDebug("getCursorCapsMode($reqModes") }
365+
if (DEBUG) { logDebug("getCursorCapsMode($reqModes)") }
366366
return TextUtils.getCapsMode(mTextFieldValue.text, mTextFieldValue.selection.min, reqModes)
367367
}
368368

0 commit comments

Comments
 (0)