|
@@ -18,80 +18,435 @@
|
|
|
|
|
|
|
|
package autosuggest;
|
|
package autosuggest;
|
|
|
|
|
|
|
|
|
|
+import android.content.Context;
|
|
|
|
|
+import android.os.Bundle;
|
|
|
|
|
+import android.os.Handler;
|
|
|
|
|
+import android.text.Editable;
|
|
|
|
|
+import android.text.Selection;
|
|
|
|
|
+import android.text.SpannableStringBuilder;
|
|
|
import android.util.Log;
|
|
import android.util.Log;
|
|
|
import android.view.KeyEvent;
|
|
import android.view.KeyEvent;
|
|
|
-import android.view.View;
|
|
|
|
|
import android.view.inputmethod.BaseInputConnection;
|
|
import android.view.inputmethod.BaseInputConnection;
|
|
|
-import android.inputmethodservice.InputMethodService;
|
|
|
|
|
-
|
|
|
|
|
-// setComposingText() - change text being composed
|
|
|
|
|
-
|
|
|
|
|
-// See 30b299cb04b4ba2330ef61a8a24c1e58513a0af2
|
|
|
|
|
-// content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
|
|
|
|
|
|
|
+import android.view.inputmethod.InputConnection;
|
|
|
|
|
+import android.view.inputmethod.InputContentInfo;
|
|
|
|
|
+import android.view.inputmethod.EditorInfo;
|
|
|
|
|
+import android.view.View;
|
|
|
|
|
+import android.view.inputmethod.CompletionInfo;
|
|
|
|
|
+import android.view.inputmethod.CorrectionInfo;
|
|
|
|
|
+import android.view.inputmethod.ExtractedText;
|
|
|
|
|
+import android.view.inputmethod.ExtractedTextRequest;
|
|
|
|
|
+import android.view.inputmethod.SurroundingText;
|
|
|
|
|
+import android.view.inputmethod.InputMethodManager;
|
|
|
|
|
+//import android.view.inputmethod.TextSnapshot;
|
|
|
|
|
+//import android.view.inputmethod.TextAttribute;
|
|
|
|
|
|
|
|
|
|
+// This InputConnection is created by ContentView.onCreateInputConnection.
|
|
|
|
|
+// It then adapts android's IME to chrome's RenderWidgetHostView using the
|
|
|
|
|
+// native ImeAdapterAndroid via the outer class ImeAdapter.
|
|
|
public class CustomInputConnection extends BaseInputConnection {
|
|
public class CustomInputConnection extends BaseInputConnection {
|
|
|
|
|
+ private static final boolean DEBUG = false;
|
|
|
|
|
+
|
|
|
|
|
+ private View mInternalView;
|
|
|
|
|
+ //private ImeAdapter mImeAdapter;
|
|
|
|
|
+ private Editable mEditable;
|
|
|
|
|
+ private boolean mSingleLine;
|
|
|
|
|
+ private int numBatchEdits;
|
|
|
|
|
+ private boolean shouldUpdateImeSelection;
|
|
|
|
|
|
|
|
native static void setup();
|
|
native static void setup();
|
|
|
- native static void onCommitText(String text);
|
|
|
|
|
- native static void onEndEdit(String text);
|
|
|
|
|
|
|
+ native static void onCompose(String text, int newCursorPos, boolean isCommit);
|
|
|
|
|
+ native static void onSetComposeRegion(int start, int end);
|
|
|
|
|
+
|
|
|
|
|
+ //private AdapterInputConnection(View view, ImeAdapter imeAdapter, EditorInfo outAttrs) {
|
|
|
|
|
+ public CustomInputConnection(View view, EditorInfo outAttrs) {
|
|
|
|
|
+ super(view, true);
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "CustomInputConnection()");
|
|
|
|
|
+ mInternalView = view;
|
|
|
|
|
+ //mImeAdapter = imeAdapter;
|
|
|
|
|
+ //mImeAdapter.setInputConnection(this);
|
|
|
|
|
+ mSingleLine = true;
|
|
|
|
|
+ outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
|
|
|
|
|
+ outAttrs.inputType = EditorInfo.TYPE_CLASS_TEXT
|
|
|
|
|
+ | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
|
|
|
|
|
+ /*
|
|
|
|
|
+ if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeText) {
|
|
|
|
|
+ */
|
|
|
|
|
+ // Normal text field
|
|
|
|
|
+ outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO;
|
|
|
|
|
+ /*
|
|
|
|
|
+ } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeTextArea ||
|
|
|
|
|
+ imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeContentEditable) {
|
|
|
|
|
+ // TextArea or contenteditable.
|
|
|
|
|
+ outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
|
|
|
|
|
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
|
|
|
|
|
+ | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT;
|
|
|
|
|
+ outAttrs.imeOptions |= EditorInfo.IME_ACTION_NONE;
|
|
|
|
|
+ mSingleLine = false;
|
|
|
|
|
+ } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypePassword) {
|
|
|
|
|
+ // Password
|
|
|
|
|
+ outAttrs.inputType = InputType.TYPE_CLASS_TEXT
|
|
|
|
|
+ | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD;
|
|
|
|
|
+ outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO;
|
|
|
|
|
+ } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeSearch) {
|
|
|
|
|
+ // Search
|
|
|
|
|
+ outAttrs.imeOptions |= EditorInfo.IME_ACTION_SEARCH;
|
|
|
|
|
+ } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeUrl) {
|
|
|
|
|
+ // Url
|
|
|
|
|
+ // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so
|
|
|
|
|
+ // exclude it for now.
|
|
|
|
|
+ outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO;
|
|
|
|
|
+ } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeEmail) {
|
|
|
|
|
+ // Email
|
|
|
|
|
+ outAttrs.inputType = InputType.TYPE_CLASS_TEXT
|
|
|
|
|
+ | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
|
|
|
|
|
+ outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO;
|
|
|
|
|
+ } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeTel) {
|
|
|
|
|
+ // Telephone
|
|
|
|
|
+ // Number and telephone do not have both a Tab key and an
|
|
|
|
|
+ // action in default OSK, so set the action to NEXT
|
|
|
|
|
+ outAttrs.inputType = InputType.TYPE_CLASS_PHONE;
|
|
|
|
|
+ outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
|
|
|
|
|
+ } else if (imeAdapter.mTextInputType == ImeAdapter.sTextInputTypeNumber) {
|
|
|
|
|
+ // Number
|
|
|
|
|
+ outAttrs.inputType = InputType.TYPE_CLASS_NUMBER
|
|
|
|
|
+ | InputType.TYPE_NUMBER_VARIATION_NORMAL;
|
|
|
|
|
+ outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
|
|
|
|
|
+ }
|
|
|
|
|
+ */
|
|
|
|
|
+ //setup();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Updates the AdapterInputConnection's internal representation of the text
|
|
|
|
|
+ * being edited and its selection and composition properties. The resulting
|
|
|
|
|
+ * Editable is accessible through the getEditable() method.
|
|
|
|
|
+ * If the text has not changed, this also calls updateSelection on the InputMethodManager.
|
|
|
|
|
+ * @param text The String contents of the field being edited
|
|
|
|
|
+ * @param selectionStart The character offset of the selection start, or the caret
|
|
|
|
|
+ * position if there is no selection
|
|
|
|
|
+ * @param selectionEnd The character offset of the selection end, or the caret
|
|
|
|
|
+ * position if there is no selection
|
|
|
|
|
+ * @param compositionStart The character offset of the composition start, or -1
|
|
|
|
|
+ * if there is no composition
|
|
|
|
|
+ * @param compositionEnd The character offset of the composition end, or -1
|
|
|
|
|
+ * if there is no selection
|
|
|
|
|
+ */
|
|
|
|
|
+ public void setEditableText(String text, int selectionStart, int selectionEnd,
|
|
|
|
|
+ int compositionStart, int compositionEnd) {
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "setEditableText(" + text + ", " + selectionStart
|
|
|
|
|
+ + ", " + selectionEnd + ", " + compositionStart
|
|
|
|
|
+ + ", " + compositionEnd + ")");
|
|
|
|
|
+
|
|
|
|
|
+ if (mEditable == null) {
|
|
|
|
|
+ mEditable = Editable.Factory.getInstance().newEditable("");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int prevSelectionStart = Selection.getSelectionStart(mEditable);
|
|
|
|
|
+ int prevSelectionEnd = Selection.getSelectionEnd(mEditable);
|
|
|
|
|
+ int prevEditableLength = mEditable.length();
|
|
|
|
|
+ int prevCompositionStart = getComposingSpanStart(mEditable);
|
|
|
|
|
+ int prevCompositionEnd = getComposingSpanEnd(mEditable);
|
|
|
|
|
+ String prevText = mEditable.toString();
|
|
|
|
|
+
|
|
|
|
|
+ selectionStart = Math.min(selectionStart, text.length());
|
|
|
|
|
+ selectionEnd = Math.min(selectionEnd, text.length());
|
|
|
|
|
+ compositionStart = Math.min(compositionStart, text.length());
|
|
|
|
|
+ compositionEnd = Math.min(compositionEnd, text.length());
|
|
|
|
|
+
|
|
|
|
|
+ boolean textUnchanged = prevText.equals(text);
|
|
|
|
|
+
|
|
|
|
|
+ if (textUnchanged
|
|
|
|
|
+ && prevSelectionStart == selectionStart && prevSelectionEnd == selectionEnd
|
|
|
|
|
+ && prevCompositionStart == compositionStart
|
|
|
|
|
+ && prevCompositionEnd == compositionEnd) {
|
|
|
|
|
+ // Nothing has changed; don't need to do anything
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // When a programmatic change has been made to the editable field, both the start
|
|
|
|
|
+ // and end positions for the composition will equal zero. In this case we cancel the
|
|
|
|
|
+ // active composition in the editor as this no longer is relevant.
|
|
|
|
|
+ if (textUnchanged && compositionStart == 0 && compositionEnd == 0) {
|
|
|
|
|
+ cancelComposition();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!textUnchanged) {
|
|
|
|
|
+ mEditable.replace(0, mEditable.length(), text);
|
|
|
|
|
+ }
|
|
|
|
|
+ Selection.setSelection(mEditable, selectionStart, selectionEnd);
|
|
|
|
|
+ super.setComposingRegion(compositionStart, compositionEnd);
|
|
|
|
|
+
|
|
|
|
|
+ if (textUnchanged || prevText.equals("")) {
|
|
|
|
|
+ // updateSelection should be called when a manual selection change occurs.
|
|
|
|
|
+ // Should not be called if text is being entered else issues can occur
|
|
|
|
|
+ // e.g. backspace to undo autocorrection will not work with the default OSK.
|
|
|
|
|
+ getInputMethodManager().updateSelection(mInternalView,
|
|
|
|
|
+ selectionStart, selectionEnd, compositionStart, compositionEnd);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Editable getEditable() {
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "getEditable()");
|
|
|
|
|
+ if (mEditable == null) {
|
|
|
|
|
+ mEditable = Editable.Factory.getInstance().newEditable("");
|
|
|
|
|
+ Selection.setSelection(mEditable, 0);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", " -> " + editableToXml(mEditable));
|
|
|
|
|
+ return mEditable;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "setComposingText(" + text + ", " + newCursorPosition + ")");
|
|
|
|
|
+ super.setComposingText(text, newCursorPosition);
|
|
|
|
|
+ shouldUpdateImeSelection = true;
|
|
|
|
|
+ onCompose(text.toString(), newCursorPosition, false);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean commitText(CharSequence text, int newCursorPosition) {
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "commitText(" + text.toString() + ", " + newCursorPosition + ")");
|
|
|
|
|
+ super.commitText(text, newCursorPosition);
|
|
|
|
|
+ shouldUpdateImeSelection = true;
|
|
|
|
|
+ onCompose(text.toString(), newCursorPosition, text.length() > 0);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean performEditorAction(int actionCode) {
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "performEditorAction(" + actionCode + ")");
|
|
|
|
|
+ switch (actionCode) {
|
|
|
|
|
+ case EditorInfo.IME_ACTION_NEXT:
|
|
|
|
|
+ cancelComposition();
|
|
|
|
|
+ // Send TAB key event
|
|
|
|
|
+ long timeStampMs = System.currentTimeMillis();
|
|
|
|
|
+ //mImeAdapter.sendSyntheticKeyEvent(
|
|
|
|
|
+ // sEventTypeRawKeyDown, timeStampMs, KeyEvent.KEYCODE_TAB, 0);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ case EditorInfo.IME_ACTION_GO:
|
|
|
|
|
+ case EditorInfo.IME_ACTION_SEARCH:
|
|
|
|
|
+ //mImeAdapter.dismissInput(true);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return super.performEditorAction(actionCode);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean performContextMenuAction(int id) {
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "performContextMenuAction(" + id + ")");
|
|
|
|
|
+ /*
|
|
|
|
|
+ switch (id) {
|
|
|
|
|
+ case android.R.id.selectAll:
|
|
|
|
|
+ return mImeAdapter.selectAll();
|
|
|
|
|
+ case android.R.id.cut:
|
|
|
|
|
+ return mImeAdapter.cut();
|
|
|
|
|
+ case android.R.id.copy:
|
|
|
|
|
+ return mImeAdapter.copy();
|
|
|
|
|
+ case android.R.id.paste:
|
|
|
|
|
+ return mImeAdapter.paste();
|
|
|
|
|
+ default:
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ */
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // Android is sending commit("foo") then edit("foo") events which is confusing.
|
|
|
|
|
- // We use this to skip edit("foo") when proceeded by the commit.
|
|
|
|
|
- private String lastCommitText;
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "getExtractedText(...)");
|
|
|
|
|
+ ExtractedText et = new ExtractedText();
|
|
|
|
|
+ if (mEditable == null) {
|
|
|
|
|
+ et.text = "";
|
|
|
|
|
+ } else {
|
|
|
|
|
+ et.text = mEditable.toString();
|
|
|
|
|
+ et.partialEndOffset = mEditable.length();
|
|
|
|
|
+ et.selectionStart = Selection.getSelectionStart(mEditable);
|
|
|
|
|
+ et.selectionEnd = Selection.getSelectionEnd(mEditable);
|
|
|
|
|
+ }
|
|
|
|
|
+ et.flags = mSingleLine ? ExtractedText.FLAG_SINGLE_LINE : 0;
|
|
|
|
|
+ return et;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- public CustomInputConnection(View view, boolean fullEditor) {
|
|
|
|
|
- super(view, fullEditor);
|
|
|
|
|
- lastCommitText = null;
|
|
|
|
|
- setup();
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean deleteSurroundingText(int leftLength, int rightLength) {
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "deleteSurroundingText(" + leftLength + ", " + rightLength + ")");
|
|
|
|
|
+ if (!super.deleteSurroundingText(leftLength, rightLength)) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ shouldUpdateImeSelection = true;
|
|
|
|
|
+ //return mImeAdapter.deleteSurroundingText(leftLength, rightLength);
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
public boolean sendKeyEvent(KeyEvent event) {
|
|
public boolean sendKeyEvent(KeyEvent event) {
|
|
|
int action = event.getAction();
|
|
int action = event.getAction();
|
|
|
int keycode = event.getKeyCode();
|
|
int keycode = event.getKeyCode();
|
|
|
- // If this is backspace/del or if the key has a character representation,
|
|
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "sendKeyEvent() action=" + action + ", keycode=" + keycode);
|
|
|
|
|
+
|
|
|
|
|
+ //mImeAdapter.mSelectionHandleController.hideAndDisallowAutomaticShowing();
|
|
|
|
|
+ //mImeAdapter.mInsertionHandleController.hideAndDisallowAutomaticShowing();
|
|
|
|
|
+
|
|
|
|
|
+ // If this is a key-up, and backspace/del or if the key has a character representation,
|
|
|
// need to update the underlying Editable (i.e. the local representation of the text
|
|
// need to update the underlying Editable (i.e. the local representation of the text
|
|
|
- // being edited). Some IMEs like Jellybean stock IME and Samsung IME mix in delete
|
|
|
|
|
- // KeyPress events instead of calling deleteSurroundingText.
|
|
|
|
|
- if (action == KeyEvent.ACTION_DOWN && keycode == KeyEvent.KEYCODE_DEL) {
|
|
|
|
|
- deleteSurroundingText(1, 0);
|
|
|
|
|
-
|
|
|
|
|
- //String text = getTextBeforeCursor(100, 0).toString();
|
|
|
|
|
- //text = text.substring(0, text.length() - 1);
|
|
|
|
|
- //setComposingText(text, 1);
|
|
|
|
|
- } else if (action == KeyEvent.ACTION_DOWN && keycode == KeyEvent.KEYCODE_FORWARD_DEL) {
|
|
|
|
|
- deleteSurroundingText(0, 1);
|
|
|
|
|
- } else if (action == KeyEvent.ACTION_DOWN && keycode == KeyEvent.KEYCODE_ENTER) {
|
|
|
|
|
- reset();
|
|
|
|
|
|
|
+ // being edited).
|
|
|
|
|
+ if (event.getAction() == KeyEvent.ACTION_UP) {
|
|
|
|
|
+ if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
|
|
|
|
|
+ super.deleteSurroundingText(1, 0);
|
|
|
|
|
+ } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) {
|
|
|
|
|
+ super.deleteSurroundingText(0, 1);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ int unicodeChar = event.getUnicodeChar();
|
|
|
|
|
+ if (unicodeChar != 0) {
|
|
|
|
|
+ Editable editable = getEditable();
|
|
|
|
|
+ int selectionStart = Selection.getSelectionStart(editable);
|
|
|
|
|
+ int selectionEnd = Selection.getSelectionEnd(editable);
|
|
|
|
|
+ if (selectionStart > selectionEnd) {
|
|
|
|
|
+ int temp = selectionStart;
|
|
|
|
|
+ selectionStart = selectionEnd;
|
|
|
|
|
+ selectionEnd = temp;
|
|
|
|
|
+ }
|
|
|
|
|
+ editable.replace(selectionStart, selectionEnd,
|
|
|
|
|
+ Character.toString((char)unicodeChar));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+ shouldUpdateImeSelection = true;
|
|
|
|
|
+ return super.sendKeyEvent(event);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean finishComposingText() {
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "finishComposingText()");
|
|
|
|
|
+ if (mEditable == null
|
|
|
|
|
+ || (getComposingSpanStart(mEditable) == getComposingSpanEnd(mEditable))) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ super.finishComposingText();
|
|
|
|
|
+ onCompose("", 0, true);
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
- public boolean commitText(CharSequence text, int newCursorPosition) {
|
|
|
|
|
- //Log.i("darkfi", String.format("commitText(%s, %d)", text.toString(), newCursorPosition));
|
|
|
|
|
- lastCommitText = text.toString();
|
|
|
|
|
- onCommitText(lastCommitText);
|
|
|
|
|
- return super.commitText(text, newCursorPosition);
|
|
|
|
|
|
|
+ public boolean setSelection(int start, int end) {
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "setSelection(" + start + ", " + end + ")");
|
|
|
|
|
+ if (start < 0 || end < 0) return true;
|
|
|
|
|
+ super.setSelection(start, end);
|
|
|
|
|
+ shouldUpdateImeSelection = true;
|
|
|
|
|
+ //return mImeAdapter.setEditableSelectionOffsets(start, end);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Informs the InputMethodManager and InputMethodSession (i.e. the IME) that there
|
|
|
|
|
+ * is no longer a current composition. Note this differs from finishComposingText, which
|
|
|
|
|
+ * is called by the IME when it wants to end a composition.
|
|
|
|
|
+ */
|
|
|
|
|
+ void cancelComposition() {
|
|
|
|
|
+ getInputMethodManager().restartInput(mInternalView);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean setComposingRegion(int start, int end) {
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "setComposingRegion(" + start + ", " + end + ")");
|
|
|
|
|
+ int a = Math.min(start, end);
|
|
|
|
|
+ int b = Math.max(start, end);
|
|
|
|
|
+ super.setComposingRegion(a, b);
|
|
|
|
|
+ onSetComposeRegion(a, b);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ boolean isActive() {
|
|
|
|
|
+ return getInputMethodManager().isActive();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private InputMethodManager getInputMethodManager() {
|
|
|
|
|
+ return (InputMethodManager) mInternalView.getContext()
|
|
|
|
|
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void updateImeSelection() {
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "updateImeSelection()");
|
|
|
|
|
+ if (mEditable != null) {
|
|
|
|
|
+ getInputMethodManager().updateSelection(mInternalView,
|
|
|
|
|
+ Selection.getSelectionStart(mEditable),
|
|
|
|
|
+ Selection.getSelectionEnd(mEditable),
|
|
|
|
|
+ getComposingSpanStart(mEditable),
|
|
|
|
|
+ getComposingSpanEnd(mEditable));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean beginBatchEdit() {
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "beginBatchEdit");
|
|
|
|
|
+ ++numBatchEdits;
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
public boolean endBatchEdit() {
|
|
public boolean endBatchEdit() {
|
|
|
- //Log.i("darkfi", "endBatchEdit: " + curr);
|
|
|
|
|
- String text = getTextBeforeCursor(100, 0).toString();
|
|
|
|
|
- if (!text.equals(lastCommitText))
|
|
|
|
|
- onEndEdit(text);
|
|
|
|
|
- lastCommitText = null;
|
|
|
|
|
- return super.endBatchEdit();
|
|
|
|
|
|
|
+ if (DEBUG) Log.d("darkfi", "endBatchEdit");
|
|
|
|
|
+ if (--numBatchEdits == 0 && shouldUpdateImeSelection) {
|
|
|
|
|
+ updateImeSelection();
|
|
|
|
|
+ shouldUpdateImeSelection = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public void reset() {
|
|
|
|
|
- setComposingText("", 0);
|
|
|
|
|
|
|
+ private String editableToXml(Editable editable) {
|
|
|
|
|
+ StringBuilder xmlBuilder = new StringBuilder();
|
|
|
|
|
+ int length = editable.length();
|
|
|
|
|
+
|
|
|
|
|
+ Object[] spans = editable.getSpans(0, editable.length(), Object.class);
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = 0; i < length; i++) {
|
|
|
|
|
+ // Find spans starting at this position
|
|
|
|
|
+ for (Object span : spans) {
|
|
|
|
|
+ if (editable.getSpanStart(span) == i) {
|
|
|
|
|
+ xmlBuilder
|
|
|
|
|
+ .append("<")
|
|
|
|
|
+ .append(span.getClass().getSimpleName())
|
|
|
|
|
+ .append(">");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Append the character
|
|
|
|
|
+ xmlBuilder.append(editable.charAt(i));
|
|
|
|
|
+
|
|
|
|
|
+ // Find spans ending at this position
|
|
|
|
|
+ for (Object span : spans) {
|
|
|
|
|
+ if (editable.getSpanEnd(span) == i) {
|
|
|
|
|
+ xmlBuilder
|
|
|
|
|
+ .append("</")
|
|
|
|
|
+ .append(span.getClass().getSimpleName())
|
|
|
|
|
+ .append(">");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Find spans starting at this position
|
|
|
|
|
+ for (Object span : spans) {
|
|
|
|
|
+ if (editable.getSpanStart(span) == length) {
|
|
|
|
|
+ xmlBuilder
|
|
|
|
|
+ .append("<")
|
|
|
|
|
+ .append(span.getClass().getSimpleName())
|
|
|
|
|
+ .append(">");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // Find spans ending at this position
|
|
|
|
|
+ for (Object span : spans) {
|
|
|
|
|
+ if (editable.getSpanEnd(span) == length) {
|
|
|
|
|
+ xmlBuilder
|
|
|
|
|
+ .append("</")
|
|
|
|
|
+ .append(span.getClass().getSimpleName())
|
|
|
|
|
+ .append(">");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // Chromium does this but the above seems to work too.
|
|
|
|
|
- //beginBatchEdit();
|
|
|
|
|
- //finishComposingText();
|
|
|
|
|
- //endBatchEdit();
|
|
|
|
|
|
|
+ return xmlBuilder.toString();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|