diff --git a/embedding/android/GeckoInputConnection.java b/embedding/android/GeckoInputConnection.java index f4637727233a..36fdf0371629 100644 --- a/embedding/android/GeckoInputConnection.java +++ b/embedding/android/GeckoInputConnection.java @@ -57,6 +57,27 @@ public class GeckoInputConnection extends BaseInputConnection implements TextWatcher { + private class ChangeNotification { + public String mText; + public int mStart; + public int mEnd; + public int mNewEnd; + + ChangeNotification(String text, int start, int oldEnd, int newEnd) { + mText = text; + mStart = start; + mEnd = oldEnd; + mNewEnd = newEnd; + } + + ChangeNotification(int start, int end) { + mText = null; + mStart = start; + mEnd = end; + mNewEnd = 0; + } + } + public GeckoInputConnection (View targetView) { super(targetView, true); mQueryResult = new SynchronousQueue(); @@ -65,7 +86,7 @@ public class GeckoInputConnection @Override public boolean beginBatchEdit() { //Log.d("GeckoAppJava", "IME: beginBatchEdit"); - + mBatchMode = true; return true; } @@ -89,6 +110,8 @@ public class GeckoInputConnection @Override public boolean deleteSurroundingText(int leftLength, int rightLength) { //Log.d("GeckoAppJava", "IME: deleteSurroundingText"); + if (leftLength == 0 && rightLength == 0) + return true; /* deleteSurroundingText is supposed to ignore the composing text, so we cancel any pending composition, delete the text, and then @@ -141,6 +164,21 @@ public class GeckoInputConnection public boolean endBatchEdit() { //Log.d("GeckoAppJava", "IME: endBatchEdit"); + mBatchMode = false; + + if (!mBatchChanges.isEmpty()) { + InputMethodManager imm = (InputMethodManager) + GeckoApp.surfaceView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + for (ChangeNotification n : mBatchChanges) { + if (n.mText != null) + notifyTextChange(imm, n.mText, n.mStart, n.mEnd, n.mNewEnd); + else + notifySelectionChange(imm, n.mStart, n.mEnd); + } + } + mBatchChanges.clear(); + } return true; } @@ -159,10 +197,12 @@ public class GeckoInputConnection mComposing = false; mComposingText = ""; - // Make sure caret stays at the same position - GeckoAppShell.sendEventToGecko( - new GeckoEvent(GeckoEvent.IME_SET_SELECTION, - mCompositionStart + mCompositionSelStart, 0)); + if (!mBatchMode) { + // Make sure caret stays at the same position + GeckoAppShell.sendEventToGecko( + new GeckoEvent(GeckoEvent.IME_SET_SELECTION, + mCompositionStart + mCompositionSelStart, 0)); + } } return true; } @@ -456,6 +496,41 @@ public class GeckoInputConnection new GeckoEvent(mCompositionSelStart + mCompositionSelLen, 0, GeckoEvent.IME_RANGE_CARETPOSITION, 0, 0, 0, mComposingText)); + + return true; + } + + @Override + public boolean setComposingRegion(int start, int end) { + //Log.d("GeckoAppJava", "IME: setComposingRegion(start=" + start + ", end=" + end + ")"); + if (start < 0 || end < start) + return true; + + CharSequence text = null; + if (start == mCompositionStart && end - start == mComposingText.length()) { + // Use mComposingText to avoid extra call to Gecko + text = mComposingText; + } + + finishComposingText(); + + if (text == null && start < end) { + GeckoAppShell.sendEventToGecko( + new GeckoEvent(GeckoEvent.IME_GET_TEXT, start, end - start)); + try { + text = mQueryResult.take(); + } catch (InterruptedException e) { + Log.e("GeckoAppJava", "IME: setComposingRegion interrupted", e); + return false; + } + } + + GeckoAppShell.sendEventToGecko( + new GeckoEvent(GeckoEvent.IME_SET_SELECTION, start, end - start)); + + // Call setComposingText with the same text to start composition and let Gecko know about new composing region + setComposingText(text, 1); + return true; } @@ -512,6 +587,11 @@ public class GeckoInputConnection // Log.d("GeckoAppShell", String.format("IME: notifyTextChange: text=%s s=%d ne=%d oe=%d", // text, start, newEnd, oldEnd)); + if (mBatchMode) { + mBatchChanges.add(new ChangeNotification(text, start, oldEnd, newEnd)); + return; + } + mNumPendingChanges = Math.max(mNumPendingChanges - 1, 0); // If there are pending changes, that means this text is not the most up-to-date version @@ -543,6 +623,10 @@ public class GeckoInputConnection public void notifySelectionChange(InputMethodManager imm, int start, int end) { // Log.d("GeckoAppJava", String.format("IME: notifySelectionChange: s=%d e=%d", start, end)); + if (mBatchMode) { + mBatchChanges.add(new ChangeNotification(start, end)); + return; + } if (mComposing) imm.updateSelection(GeckoApp.surfaceView, @@ -569,6 +653,8 @@ public class GeckoInputConnection mComposingText = ""; mUpdateRequest = null; mNumPendingChanges = 0; + mBatchMode = false; + mBatchChanges.clear(); } // TextWatcher @@ -629,6 +715,10 @@ public class GeckoInputConnection // Number of in flight changes int mNumPendingChanges; + boolean mBatchMode; + private CopyOnWriteArrayList mBatchChanges = + new CopyOnWriteArrayList(); + ExtractedTextRequest mUpdateRequest; final ExtractedText mUpdateExtract = new ExtractedText();