@Override public boolean setSelection(int start, int end) { GeckoAppShell.sendEventToGecko( GeckoEvent.createIMEEvent(GeckoEvent.IME_SET_SELECTION, start, end - start)); return super.setSelection(start, end); }
private boolean processKeyDown(int keyCode, KeyEvent event, boolean isPreIme) { if (DEBUG) { Log.d( LOGTAG, "IME: processKeyDown(keyCode=" + keyCode + ", event=" + event + ", " + isPreIme + ")"); } clampSelection(); switch (keyCode) { case KeyEvent.KEYCODE_MENU: case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_SEARCH: return false; case KeyEvent.KEYCODE_DEL: // See comments in GeckoInputConnection.onKeyDel if (onKeyDel()) { return true; } break; case KeyEvent.KEYCODE_ENTER: if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0 && mIMEActionHint.equalsIgnoreCase("next")) event = new KeyEvent(event.getAction(), KeyEvent.KEYCODE_TAB); break; default: break; } if (isPreIme && mIMEState != IME_STATE_DISABLED && (event.getMetaState() & KeyEvent.META_ALT_ON) != 0) // Let active IME process pre-IME key events return false; View view = GeckoApp.mAppContext.getLayerController().getView(); KeyListener keyListener = TextKeyListener.getInstance(); // KeyListener returns true if it handled the event for us. if (mIMEState == IME_STATE_DISABLED || keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_TAB || (event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 || !keyListener.onKeyDown(view, mEditable, keyCode, event)) { // Make sure selection in Gecko is up-to-date final Editable content = getEditable(); int a = Selection.getSelectionStart(content); int b = Selection.getSelectionEnd(content); GeckoAppShell.sendEventToGecko( GeckoEvent.createIMEEvent(GeckoEvent.IME_SET_SELECTION, a, b - a)); GeckoAppShell.sendEventToGecko(GeckoEvent.createKeyEvent(event)); } return true; }
@Override public boolean performContextMenuAction(int id) { final Editable content = getEditable(); if (content == null) return false; String text = content.toString(); clampSelection(); int a = Selection.getSelectionStart(content); int b = Selection.getSelectionEnd(content); switch (id) { case R.id.selectAll: setSelection(0, text.length()); break; case R.id.cut: // Fill the clipboard GeckoAppShell.setClipboardText(text); // If selection is empty, we'll select everything if (a >= b) GeckoAppShell.sendEventToGecko( GeckoEvent.createIMEEvent(GeckoEvent.IME_SET_SELECTION, 0, text.length())); GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEEvent(GeckoEvent.IME_DELETE_TEXT, 0, 0)); break; case R.id.paste: commitText(GeckoAppShell.getClipboardText(), 1); break; case R.id.copy: // If there is no selection set, we must be doing "Copy All", // otherwise get the selection if (a < b) text = text.substring(a, b); GeckoAppShell.setClipboardText(text.substring(a, b)); break; } return true; }
private void endComposition() { if (DEBUG) Log.d(LOGTAG, "IME: endComposition: IME_COMPOSITION_END"); GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEEvent(GeckoEvent.IME_COMPOSITION_END, 0, 0)); mCompositionStart = NO_COMPOSITION_STRING; }
// TextWatcher public void onTextChanged(CharSequence s, int start, int before, int count) { if (hasCompositionString() && mCompositionStart != start) { // Changed range is different from the composition, need to reset the composition endComposition(); } CharSequence changedText = s.subSequence(start, start + count); if (changedText.length() == 1) { char changedChar = changedText.charAt(0); // Some IMEs (e.g. SwiftKey X) send a string with '\n' when Enter is pressed // Such string cannot be handled by Gecko, so we convert it to a key press instead if (changedChar == '\n') { processKeyDown( KeyEvent.KEYCODE_ENTER, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER), false); processKeyUp( KeyEvent.KEYCODE_ENTER, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER), false); return; } // If we are committing a single character and didn't have an active composition string, // we can send Gecko keydown/keyup events instead of composition events. if (mCommittingText && !hasCompositionString() && synthesizeKeyEvents(changedChar)) { // Block this thread until all pending events are processed GeckoAppShell.geckoEventSync(); return; } } if (!hasCompositionString()) { if (DEBUG) Log.d(LOGTAG, ". . . onTextChanged: IME_COMPOSITION_BEGIN"); GeckoAppShell.sendEventToGecko( GeckoEvent.createIMEEvent(GeckoEvent.IME_COMPOSITION_BEGIN, 0, 0)); mCompositionStart = start; if (DEBUG) { Log.d(LOGTAG, ". . . onTextChanged: IME_SET_SELECTION, start=" + start + ", len=" + before); } GeckoAppShell.sendEventToGecko( GeckoEvent.createIMEEvent(GeckoEvent.IME_SET_SELECTION, start, before)); } sendTextToGecko(changedText, start + count); if (DEBUG) { Log.d(LOGTAG, ". . . onTextChanged: IME_SET_SELECTION, start=" + (start + count) + ", 0"); } GeckoAppShell.sendEventToGecko( GeckoEvent.createIMEEvent(GeckoEvent.IME_SET_SELECTION, start + count, 0)); // End composition if all characters in the word have been deleted. // This fixes autocomplete results not appearing. if (count == 0) endComposition(); // Block this thread until all pending events are processed GeckoAppShell.geckoEventSync(); }