Search code examples
androidkotlinandroid-edittextandroid-softkeyboardinputconnection

How can you listen to soft key events sent to a View with an InputConnection?


I would like to create a custom text editor view in Android, with customizable key controls, meaning that I need to handle all software keyboard key events.

To do this, I can extend View and override its onKeyDown event. However, I would also like to preserve the user's full software keyboard functionality, e.g. suggestions and glide-typing, which seems to only work if I properly override onCreateInputConnection like so:

override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection {
    outAttrs.inputType = InputType.TYPE_CLASS_TEXT
    return object : BaseInputConnection(this, true) { ... }
}

Unfortunately, the InputConnection seems to eat all the key events and block the key listener methods like onKeyDown from being called, whereas I would like to listen to key events and use an InputConnection (to allow full soft keyboard functionality) at the same time.

The only workaround I have found is using a TextWatcher to listen to text change events, but I don't feel this is as flexible or simple as I need it to be; I need full and direct access to all key presses.

I believe this is possible because this is exactly how an input element in a WebView works: I can listen to its key events and the user can input text with suggestions or glide-typing at the same time. In fact, I am considering using a WebView after all, but I don't see why it shouldn't be possible to do this without one.

How can I implement this behavior in Android, in either a View or a Composable?


Solution

  • KeyEvents don't exist when using a soft keyboard. Almost every soft keyboard uses InputConnection.commitText() to send characters or even words at a time. KeyEvents are only really generated by hardware keyboards, bluetooth keyboards, and physical keys like volume.

    If you want to look for changes to text, implement the commitText() function and see what's sent over there. There's one or two other functions like deleteSurroundingText you'd need to override as well to get delete presses.