Suragch Suragch - 26 days ago 9
Android Question

Setting the cursor at the start of inserted text with InputConnection.commitText

The documentation for

says that
newCursorPosition
means:


int: The new cursor position around the text, in Java characters. If >
0, this is relative to the end of the text - 1; if <= 0, this is
relative to the start of the text.
So a value of 1 will always advance
the cursor to the position after the full text being inserted. Note
that this means you can't position the cursor within the text, because
the editor can make modifications to the text you are providing so it
is not possible to correctly specify locations there.


In this example, if I enter two characters, then position the cursor between them like this

enter image description here

and then enter another character, it doesn't matter if I set
newCursorPosition
to
0
or
1
. The cursor is always at the end of the insertion. For example calling

inputConnection.commitText("aaa", 0);


or

inputConnection.commitText("aaa", 1);


Both show the cursor like this:

enter image description here

If I do
-1
with

inputConnection.commitText("aaa", -1);


I get this

enter image description here

The
1
and
-1
results are expected as per the documentation. Why doesn't
0
put the cursor at the beginning of the insertion?
I would expect
0
should be like this

inputConnection.commitText("aaa", 0);


enter image description here

but it isn't. Why not?

Answer Source

This looks like a defect in the code, but you be the judge.

Take a look at replaceText() in BaseInputConnection. I believe that this is the code that places the cursor after insertion. (replaceText() is called from commitText()).

In the referenced code, a is the selection start. b is the selection end. Since there is no selection in the example and the cursor is at index 1 then a == b == 1. Also, the new text (aaa) is not inserted (replacing a selection [a,b]) until after the cursor is moved to the new selection.

Selection.setSelection(content, newCursorPosition) sets the cursor position, so for 0 and 1 to produce identical positioning in your example, I would expect the derived value of newCursorPosition to be the same for both inputs.

With the cursor positioned between the two 8's at position 1, let's think through the following code:

if (newCursorPosition > 0) {
    newCursorPosition += b - 1;
} else {
    newCursorPosition += a;
}

For your input of 1, newCursorPosition > 0, so newCursorPosition = newCursorPosition + 1 - 1 or 1.

For your input of 0, newCursorPosition is not = 0, so newCursorPosition = newCursorPosition + a (0 + 1) or 1.

Since both inputs produce the same value, I would expect Selection.setSelection(content, newCursorPosition) to produce the results you see.

I have not followed the code exactly to this location, but I believe that this is the problem. I have followed the execution paths in BaseInputConnection for newCursorPosition = 0 and newCursorPosition = 1 on a Pixel emulator with API 21 and what is outlined above does hold.