Search code examples
androidkotlinimeandroid-input-method

Is InputMethodService.onUpdateSelection asynchronous?


I am making a custom IME. I am trying to track when the user manually changes the selection region of text (as opposed to the selection changing as a result of actions from the IME itself, like commitText).

To do this, I am keeping track of where the IME expects the selection position to be, and compares it to the actual selection position from onUpdateSelection.

For example, to commit text I use:

private fun commitTextToInputConnection(text: String)
{
    moveExpectedSelection(text.length)
    service.currentInputConnection.commitText(text, 1)
}

However, if I do:

commitTextToInputConnection("Test1")
commitTextToInputConnection("Test2")

I find that, in order, the following sequence occurs:

1 - ExpectedSelectionPosition updates for "Test1"

2 - ExpectedSelectionPosition updates for "Test2"

3 - onUpdateSelection for "Test1" is called

4 - onUpdateSelection for "Test2" is called

Obviously, this order is incorrect, and leads to my IME having an incorrect ExpectedSelectionPosition.

The strangest thing is that, for some Activities, the sequence of ExpectedSelectionPosition updates and onUpdateSelection calls always occur in the correct order. For other Activities, they consistently occur in the same (wrong) order.

What is going on here? I'm guessing commitText, etc, must be asynchronous, leading to this race condition, but this is not mentioned at all in the documentation.

Are there are any work-arounds to this issue? Or, are there any other ways I can listen exclusively for changes in text selection triggered manually by the user, and not the IME?


Solution

  • The solution was to use InputConnection.beginBatchEdit and InputConnection.endBatchEdit.

    Any changes made to the currentInputConnection within a 'batchEdit' block are lumped together into a single onUpdateSelection call, at the end.

    For example, when the following is executed:

    service.currentInputConnection.beginBatchEdit()
    commitTextToInputConnection("Test1")
    commitTextToInputConnection("Test2")
    service.currentInputConnection.endBatchEdit()
    

    onUpdateSelection is called only once, after all the changes in the block have been made.