From 545278cfdcc495a7944350a1676b631702a43840 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Thu, 1 Nov 2012 16:11:03 -0400 Subject: [PATCH] Bug 805162 - f. Reimplement InputConnection methods using GeckoEditable; r=cpeterson --- mobile/android/base/GeckoInputConnection.java | 335 ++---------------- 1 file changed, 34 insertions(+), 301 deletions(-) diff --git a/mobile/android/base/GeckoInputConnection.java b/mobile/android/base/GeckoInputConnection.java index 0c355a040880..754f80337e89 100644 --- a/mobile/android/base/GeckoInputConnection.java +++ b/mobile/android/base/GeckoInputConnection.java @@ -119,64 +119,25 @@ class GeckoInputConnection } @Override - public boolean beginBatchEdit() { + public synchronized boolean beginBatchEdit() { mBatchEditCount++; + mEditableClient.setUpdateGecko(false); return true; } @Override - public boolean endBatchEdit() { + public synchronized boolean endBatchEdit() { if (mBatchEditCount > 0) { mBatchEditCount--; + if (mBatchEditCount == 0) { + mEditableClient.setUpdateGecko(true); + } } else { Log.w(LOGTAG, "endBatchEdit() called, but mBatchEditCount == 0?!"); } return true; } - @Override - public boolean commitCompletion(CompletionInfo text) { - return commitText(text.getText(), 1); - } - - @Override - public boolean commitText(CharSequence text, int newCursorPosition) { - if (mCommittingText) - Log.e(LOGTAG, "Please report this bug:", - new IllegalStateException("commitText, but already committing text?!")); - - mCommittingText = true; - replaceText(text, newCursorPosition, false); - mCommittingText = false; - - if (hasCompositionString()) { - if (DEBUG) Log.d(LOGTAG, ". . . commitText: endComposition"); - endComposition(); - } - return true; - } - - @Override - public boolean finishComposingText() { - // finishComposingText() is called by the input method manager from a background - // thread so we have to make sure it's run in the ui thread. - postToUiThread(new Runnable() { - public void run() { - if (hasCompositionString()) { - if (DEBUG) Log.d(LOGTAG, ". . . finishComposingText: endComposition"); - endComposition(); - } - final Editable content = getEditable(); - if (content != null) { - beginBatchEdit(); - removeComposingSpans(content); - endBatchEdit(); - } - } - }); - return true; - } - @Override public Editable getEditable() { return mEditableClient.getEditable(); @@ -184,32 +145,38 @@ class GeckoInputConnection @Override public boolean performContextMenuAction(int id) { - String text = mEditable.toString(); - Span selection = getSelection(); + Editable editable = getEditable(); + int selStart = Selection.getSelectionStart(editable); + int selEnd = Selection.getSelectionEnd(editable); switch (id) { case R.id.selectAll: - setSelection(0, text.length()); + setSelection(0, editable.length()); break; case R.id.cut: - // Fill the clipboard - GeckoAppShell.setClipboardText(text); // If selection is empty, we'll select everything - if (selection.length == 0) - GeckoAppShell.sendEventToGecko( - GeckoEvent.createIMEEvent(GeckoEvent.IME_SET_SELECTION, 0, text.length())); - GeckoAppShell.sendEventToGecko( - GeckoEvent.createIMEEvent(GeckoEvent.IME_DELETE_TEXT, 0, 0)); + if (selStart == selEnd) { + // Fill the clipboard + GeckoAppShell.setClipboardText(editable.toString()); + editable.clear(); + } else { + GeckoAppShell.setClipboardText( + editable.toString().substring( + Math.min(selStart, selEnd), + Math.max(selStart, selEnd))); + editable.delete(selStart, selEnd); + } break; case R.id.paste: commitText(GeckoAppShell.getClipboardText(), 1); break; case R.id.copy: // Copy the current selection or the empty string if nothing is selected. - String copiedText = selection.length > 0 - ? text.substring(selection.start, selection.end) - : ""; - GeckoAppShell.setClipboardText(text); + String copiedText = selStart == selEnd ? "" : + editable.toString().substring( + Math.min(selStart, selEnd), + Math.max(selStart, selEnd)); + GeckoAppShell.setClipboardText(copiedText); break; } return true; @@ -223,184 +190,32 @@ class GeckoInputConnection if ((flags & GET_EXTRACTED_TEXT_MONITOR) != 0) mUpdateRequest = req; - Span selection = getSelection(); + Editable editable = getEditable(); + int selStart = Selection.getSelectionStart(editable); + int selEnd = Selection.getSelectionEnd(editable); ExtractedText extract = new ExtractedText(); extract.flags = 0; extract.partialStartOffset = -1; extract.partialEndOffset = -1; - extract.selectionStart = selection.start; - extract.selectionEnd = selection.end; + extract.selectionStart = selStart; + extract.selectionEnd = selEnd; extract.startOffset = 0; - extract.text = mEditable.toString(); + extract.text = editable; return extract; } - @Override - public boolean setSelection(int start, int end) { - // Some IMEs call setSelection() with negative or stale indexes, so clamp them. - Span newSelection = Span.clamp(start, end, mEditable); - GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEEvent(GeckoEvent.IME_SET_SELECTION, - newSelection.start, - newSelection.length)); - return super.setSelection(newSelection.start, newSelection.end); - } - private static void postToUiThread(Runnable runnable) { // postToUiThread() is called by the Gecko and TimerTask threads. // The UI thread does not need to post Runnables to itself. GeckoApp.mAppContext.mMainHandler.post(runnable); } - @Override - public CharSequence getTextBeforeCursor(int length, int flags) { - // Avoid underrunning text buffer. - Span selection = getSelection(); - if (length > selection.start) { - length = selection.start; - } - - if (length < 1) { - return ""; - } - - return super.getTextBeforeCursor(length, flags); - } - - @Override - public CharSequence getTextAfterCursor(int length, int flags) { - // Avoid overrunning text buffer. - Span selection = getSelection(); - int contentLength = mEditable.length(); - if (selection.end + length > contentLength) { - length = contentLength - selection.end; - } - - if (length < 1) { - return ""; - } - - return super.getTextAfterCursor(length, flags); - } - - @Override - public boolean setComposingText(CharSequence text, int newCursorPosition) { - // setComposingText() places the given text into the editable, replacing any existing - // composing text. This method will likely be called multiple times while we are composing - // text. - return super.setComposingText(text, newCursorPosition); - } - private static View getView() { return GeckoApp.mAppContext.getLayerView(); } - private Span getSelection() { - int start = Selection.getSelectionStart(mEditable); - int end = Selection.getSelectionEnd(mEditable); - return Span.clamp(start, end, mEditable); - } - - private void replaceText(CharSequence text, int newCursorPosition, boolean composing) { - if (DEBUG) { - Log.d(LOGTAG, String.format("IME: replaceText(\"%s\", %d, %b)", - text, newCursorPosition, composing)); - GeckoApp.assertOnUiThread(); - } - - if (text == null) - text = ""; - - beginBatchEdit(); - - // delete composing text set previously. - int a; - int b; - - Span composingSpan = getComposingSpan(); - if (composingSpan != null) { - removeComposingSpans(mEditable); - a = composingSpan.start; - b = composingSpan.end; - composingSpan = null; - } else { - Span selection = getSelection(); - a = selection.start; - b = selection.end; - } - - if (composing) { - Spannable sp = null; - if (!(text instanceof Spannable)) { - sp = new SpannableStringBuilder(text); - text = sp; - // Underline the active composition string. - sp.setSpan(new UnderlineSpan(), 0, sp.length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING); - } else { - sp = (Spannable) text; - } - setComposingSpans(sp); - } - - if (DEBUG) Log.d(LOGTAG, "Replacing from " + a + " to " + b + " with \"" - + text + "\", composing=" + composing - + ", type=" + text.getClass().getCanonicalName()); - - if (DEBUG) { - LogPrinter lp = new LogPrinter(Log.VERBOSE, LOGTAG); - lp.println("Current text:"); - TextUtils.dumpSpans(mEditable, lp, " "); - lp.println("Composing text:"); - TextUtils.dumpSpans(text, lp, " "); - } - - // Position the cursor appropriately, so that after replacing the - // desired range of text it will be located in the correct spot. - // This allows us to deal with filters performing edits on the text - // we are providing here. - if (newCursorPosition > 0) { - newCursorPosition += b - 1; - } else { - newCursorPosition += a; - } - if (newCursorPosition < 0) newCursorPosition = 0; - if (newCursorPosition > mEditable.length()) - newCursorPosition = mEditable.length(); - Selection.setSelection(mEditable, newCursorPosition); - - mEditable.replace(a, b, text); - - if (DEBUG) { - LogPrinter lp = new LogPrinter(Log.VERBOSE, LOGTAG); - lp.println("Final text:"); - TextUtils.dumpSpans(mEditable, lp, " "); - } - - endBatchEdit(); - } - - @Override - public boolean setComposingRegion(int start, int end) { - if (hasCompositionString()) { - if (DEBUG) Log.d(LOGTAG, ". . . setComposingRegion: endComposition"); - endComposition(); - } - - Span newComposingRegion = Span.clamp(start, end, mEditable); - return super.setComposingRegion(newComposingRegion.start, newComposingRegion.end); - } - - public String getComposingText() { - Span composingSpan = getComposingSpan(); - if (composingSpan == null || composingSpan.length == 0) { - return ""; - } - - return TextUtils.substring(mEditable, composingSpan.start, composingSpan.end); - } - public boolean onKeyDel() { // Some IMEs don't update us on deletions // In that case we are not updated when a composition @@ -481,8 +296,7 @@ class GeckoInputConnection mBatchEditCount = 0; } - removeComposingSpans(mEditable); - mCompositionStart = NO_COMPOSITION_STRING; + removeComposingSpans(getEditable()); mUpdateRequest = null; } @@ -552,13 +366,6 @@ class GeckoInputConnection | EditorInfo.IME_FLAG_NO_FULLSCREEN; } - // onCreateInputConnection() can be called during composition when input focus - // is restored from a VKB popup window (such as for entering accented characters) - // back to our IME. We want to commit our active composition string. Bug 756429 - if (hasCompositionString()) { - endComposition(); - } - String prevInputMethod = mCurrentInputMethod; mCurrentInputMethod = InputMethods.getCurrentInputMethod(app); if (DEBUG) { @@ -799,7 +606,7 @@ class GeckoInputConnection } public void run() { - if (DEBUG) Log.d(LOGTAG, "IME: run()"); + if (DEBUG) Log.d(LOGTAG, "IME: IMEStateUpdater.run()"); synchronized (IMEStateUpdater.class) { instance = null; } @@ -831,80 +638,6 @@ class GeckoInputConnection } } - private void setEditable(String contents) { - int prevLength = mEditable.length(); - mEditable.removeSpan(this); - mEditable.replace(0, prevLength, contents); - spanAndSelectEditable(); - } - - private void spanAndSelectEditable() { - int length = mEditable.length(); - mEditable.setSpan(this, 0, length, Spanned.SPAN_INCLUSIVE_INCLUSIVE); - Selection.setSelection(mEditable, length); - } - - protected final boolean hasCompositionString() { - return mCompositionStart != NO_COMPOSITION_STRING; - } - - private Span getComposingSpan() { - int start = getComposingSpanStart(mEditable); - int end = getComposingSpanEnd(mEditable); - - // Does the editable have a composing span? - if (start < 0 || end < 0) { - if (start != -1 || end != -1) { - throw new IndexOutOfBoundsException("Bad composing span [" + start + "," + end - + "), contentLength=" + mEditable.length()); - } - return null; - } - - return new Span(start, end, mEditable); - } - - private static String prettyPrintString(CharSequence s) { - // Quote string and replace newlines with CR arrows. - return "\"" + s.toString().replace('\n', UNICODE_CRARR) + "\""; - } - - private static final class Span { - public final int start; - public final int end; - public final int length; - - public static Span clamp(int start, int end, Editable content) { - return new Span(start, end, content); - } - - private Span(int a, int b, Editable content) { - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - final int contentLength = content.length(); - - if (a < 0) { - a = 0; - } else if (a > contentLength) { - a = contentLength; - } - - if (b < 0) { - b = 0; - } else if (b > contentLength) { - b = contentLength; - } - - start = a; - end = b; - length = end - start; - } - } - private static final class DebugGeckoInputConnection extends GeckoInputConnection { public DebugGeckoInputConnection(View targetView) { super(targetView);