зеркало из https://github.com/mozilla/gecko-dev.git
Bug 805162 - e. Implement GeckoEditableListener in GeckoInputConnection; r=cpeterson
This commit is contained in:
Родитель
399f6c60d4
Коммит
b1e43f738d
|
@ -44,7 +44,7 @@ import java.util.TimerTask;
|
||||||
|
|
||||||
class GeckoInputConnection
|
class GeckoInputConnection
|
||||||
extends BaseInputConnection
|
extends BaseInputConnection
|
||||||
implements TextWatcher, InputConnectionHandler {
|
implements InputConnectionHandler, GeckoEditableListener {
|
||||||
|
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
protected static final String LOGTAG = "GeckoInputConnection";
|
protected static final String LOGTAG = "GeckoInputConnection";
|
||||||
|
@ -96,21 +96,25 @@ class GeckoInputConnection
|
||||||
private boolean mCommittingText;
|
private boolean mCommittingText;
|
||||||
private KeyCharacterMap mKeyCharacterMap;
|
private KeyCharacterMap mKeyCharacterMap;
|
||||||
private final Editable mEditable;
|
private final Editable mEditable;
|
||||||
|
private final GeckoEditableClient mEditableClient;
|
||||||
protected int mBatchEditCount;
|
protected int mBatchEditCount;
|
||||||
private ExtractedTextRequest mUpdateRequest;
|
private ExtractedTextRequest mUpdateRequest;
|
||||||
private final ExtractedText mUpdateExtract = new ExtractedText();
|
private final ExtractedText mUpdateExtract = new ExtractedText();
|
||||||
|
|
||||||
public static GeckoInputConnection create(View targetView) {
|
public static InputConnectionHandler create(View targetView,
|
||||||
|
GeckoEditableClient editable) {
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
return new DebugGeckoInputConnection(targetView);
|
return DebugGeckoInputConnection.create(targetView, editable);
|
||||||
else
|
else
|
||||||
return new GeckoInputConnection(targetView);
|
return new GeckoInputConnection(targetView, editable);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected GeckoInputConnection(View targetView) {
|
protected GeckoInputConnection(View targetView,
|
||||||
|
GeckoEditableClient editable) {
|
||||||
super(targetView, true);
|
super(targetView, true);
|
||||||
mEditable = Editable.Factory.getInstance().newEditable("");
|
mEditableClient = editable;
|
||||||
spanAndSelectEditable();
|
// install the editable => input connection listener
|
||||||
|
editable.setListener(this);
|
||||||
mIMEState = IME_STATE_DISABLED;
|
mIMEState = IME_STATE_DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +179,7 @@ class GeckoInputConnection
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Editable getEditable() {
|
public Editable getEditable() {
|
||||||
return mEditable;
|
return mEditableClient.getEditable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -426,100 +430,48 @@ class GeckoInputConnection
|
||||||
return InputMethods.getInputMethodManager(context);
|
return InputMethods.getInputMethodManager(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void notifyTextChange(String text, int start, int oldEnd, int newEnd) {
|
public void onTextChange(String text, int start, int oldEnd, int newEnd) {
|
||||||
if (mBatchEditCount == 0) {
|
|
||||||
if (!text.contentEquals(mEditable)) {
|
|
||||||
if (DEBUG) Log.d(LOGTAG, ". . . notifyTextChange: current mEditable="
|
|
||||||
+ prettyPrintString(mEditable));
|
|
||||||
|
|
||||||
// Editable will be updated by IME event
|
if (mBatchEditCount > 0 || mUpdateRequest == null) {
|
||||||
if (!hasCompositionString())
|
return;
|
||||||
setEditable(text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mUpdateRequest == null)
|
final InputMethodManager imm = getInputMethodManager();
|
||||||
return;
|
if (imm == null) {
|
||||||
|
|
||||||
InputMethodManager imm = getInputMethodManager();
|
|
||||||
if (imm == null)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
final View v = getView();
|
||||||
|
final Editable editable = getEditable();
|
||||||
|
|
||||||
mUpdateExtract.flags = 0;
|
mUpdateExtract.flags = 0;
|
||||||
|
// Update from (0, oldEnd) to (0, newEnd) because some IMEs
|
||||||
// We update from (0, oldEnd) to (0, newEnd) because some Android IMEs
|
|
||||||
// assume that updates start at zero, according to jchen.
|
// assume that updates start at zero, according to jchen.
|
||||||
mUpdateExtract.partialStartOffset = 0;
|
mUpdateExtract.partialStartOffset = 0;
|
||||||
mUpdateExtract.partialEndOffset = oldEnd;
|
mUpdateExtract.partialEndOffset = editable.length();
|
||||||
|
mUpdateExtract.selectionStart =
|
||||||
String updatedText = (newEnd > text.length() ? text : text.substring(0, newEnd));
|
Selection.getSelectionStart(editable);
|
||||||
int updatedTextLength = updatedText.length();
|
mUpdateExtract.selectionEnd =
|
||||||
|
Selection.getSelectionEnd(editable);
|
||||||
// Faster to not query for selection
|
|
||||||
mUpdateExtract.selectionStart = updatedTextLength;
|
|
||||||
mUpdateExtract.selectionEnd = updatedTextLength;
|
|
||||||
|
|
||||||
mUpdateExtract.text = updatedText;
|
|
||||||
mUpdateExtract.startOffset = 0;
|
mUpdateExtract.startOffset = 0;
|
||||||
|
mUpdateExtract.text = editable;
|
||||||
|
|
||||||
View v = getView();
|
imm.updateExtractedText(v, mUpdateRequest.token,
|
||||||
imm.updateExtractedText(v, mUpdateRequest.token, mUpdateExtract);
|
mUpdateExtract);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void notifySelectionChange(int start, int end) {
|
public void onSelectionChange(int start, int end) {
|
||||||
if (mBatchEditCount == 0) {
|
|
||||||
Span newSelection = Span.clamp(start, end, mEditable);
|
|
||||||
start = newSelection.start;
|
|
||||||
end = newSelection.end;
|
|
||||||
|
|
||||||
Span currentSelection = getSelection();
|
if (mBatchEditCount > 0) {
|
||||||
int a = currentSelection.start;
|
return;
|
||||||
int b = currentSelection.end;
|
|
||||||
|
|
||||||
if (start != a || end != b) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(LOGTAG, String.format(
|
|
||||||
". . . notifySelectionChange: current editable selection: [%d, %d)",
|
|
||||||
a, b));
|
|
||||||
}
|
|
||||||
|
|
||||||
super.setSelection(start, end);
|
|
||||||
|
|
||||||
// Check if the selection is inside composing span
|
|
||||||
Span composingSpan = getComposingSpan();
|
|
||||||
if (composingSpan != null) {
|
|
||||||
int ca = composingSpan.start;
|
|
||||||
int cb = composingSpan.end;
|
|
||||||
if (start < ca || start > cb || end < ca || end > cb) {
|
|
||||||
if (DEBUG) Log.d(LOGTAG, ". . . notifySelectionChange: removeComposingSpans");
|
|
||||||
removeComposingSpans(mEditable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
final InputMethodManager imm = getInputMethodManager();
|
||||||
// FIXME: Remove this postToUiThread() after bug 780543 is fixed.
|
if (imm == null) {
|
||||||
final int oldStart = start;
|
return;
|
||||||
final int oldEnd = end;
|
}
|
||||||
postToUiThread(new Runnable() {
|
final View v = getView();
|
||||||
public void run() {
|
final Editable editable = getEditable();
|
||||||
InputMethodManager imm = getInputMethodManager();
|
imm.updateSelection(v, start, end, getComposingSpanStart(editable),
|
||||||
if (imm != null && imm.isFullscreenMode()) {
|
getComposingSpanEnd(editable));
|
||||||
int newStart;
|
|
||||||
int newEnd;
|
|
||||||
Span span = getComposingSpan();
|
|
||||||
if (span != null && hasCompositionString()) {
|
|
||||||
newStart = span.start;
|
|
||||||
newEnd = span.end;
|
|
||||||
} else {
|
|
||||||
newStart = -1;
|
|
||||||
newEnd = -1;
|
|
||||||
}
|
|
||||||
View v = getView();
|
|
||||||
imm.updateSelection(v, oldStart, oldEnd, newStart, newEnd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void resetCompositionState() {
|
protected void resetCompositionState() {
|
||||||
|
@ -534,268 +486,6 @@ class GeckoInputConnection
|
||||||
mUpdateRequest = null;
|
mUpdateRequest = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (DEBUG) {
|
|
||||||
Log.d(LOGTAG, "onTextChanged: changedText=\"" + changedText + "\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
processKeyUp(KeyEvent.KEYCODE_ENTER, new KeyEvent(KeyEvent.ACTION_UP,
|
|
||||||
KeyEvent.KEYCODE_ENTER));
|
|
||||||
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() && sendKeyEventsToGecko(changedChar)) {
|
|
||||||
// Block this thread until all pending events are processed
|
|
||||||
GeckoAppShell.geckoEventSync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean startCompositionString = !hasCompositionString();
|
|
||||||
if (startCompositionString) {
|
|
||||||
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 || (startCompositionString && mCommittingText))
|
|
||||||
endComposition();
|
|
||||||
|
|
||||||
// Block this thread until all pending events are processed
|
|
||||||
GeckoAppShell.geckoEventSync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean sendKeyEventsToGecko(char inputChar) {
|
|
||||||
// Synthesize VKB key events that could plausibly generate the input character.
|
|
||||||
KeyEvent[] events = synthesizeKeyEvents(inputChar);
|
|
||||||
if (events == null) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(LOGTAG, "synthesizeKeyEvents: char '" + inputChar
|
|
||||||
+ "' has no virtual key mapping");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean sentKeyEvents = false;
|
|
||||||
|
|
||||||
for (KeyEvent event : events) {
|
|
||||||
if (!KeyEvent.isModifierKey(event.getKeyCode())) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(LOGTAG, "synthesizeKeyEvents: char '" + inputChar
|
|
||||||
+ "' -> action=" + event.getAction()
|
|
||||||
+ ", keyCode=" + event.getKeyCode()
|
|
||||||
+ ", UnicodeChar='" + (char) event.getUnicodeChar() + "'");
|
|
||||||
}
|
|
||||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createKeyEvent(event));
|
|
||||||
sentKeyEvents = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sentKeyEvents;
|
|
||||||
}
|
|
||||||
|
|
||||||
private KeyEvent[] synthesizeKeyEvents(char inputChar) {
|
|
||||||
// Some symbol characters produce unusual key events on Froyo and Gingerbread.
|
|
||||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
|
|
||||||
switch (inputChar) {
|
|
||||||
case '&':
|
|
||||||
// Some Gingerbread devices' KeyCharacterMaps return ALT+7 instead of SHIFT+7,
|
|
||||||
// but some devices like the Droid Bionic treat SHIFT+7 as '7'. So just return
|
|
||||||
// null and onTextChanged() will send "&" as a composition string instead of
|
|
||||||
// KEY_DOWN + KEY_UP event pair. This may break web content listening for '&'
|
|
||||||
// key events, but they will still receive "&" input event.
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case '<':
|
|
||||||
case '>':
|
|
||||||
// We can't synthesize KeyEvents for '<' or '>' because Froyo and Gingerbread
|
|
||||||
// return incorrect shifted char codes from KeyEvent.getUnicodeChar().
|
|
||||||
// Send these characters as composition strings, not key events.
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Some symbol characters produce key events on Froyo and Gingerbread, but not
|
|
||||||
// Honeycomb and ICS. Send these characters as composition strings, not key events,
|
|
||||||
// to more closely mimic Honeycomb and ICS.
|
|
||||||
case UNICODE_BULLET:
|
|
||||||
case UNICODE_CENT_SIGN:
|
|
||||||
case UNICODE_COPYRIGHT_SIGN:
|
|
||||||
case UNICODE_DIVISION_SIGN:
|
|
||||||
case UNICODE_DOUBLE_LOW_QUOTATION_MARK:
|
|
||||||
case UNICODE_ELLIPSIS:
|
|
||||||
case UNICODE_EURO_SIGN:
|
|
||||||
case UNICODE_INVERTED_EXCLAMATION_MARK:
|
|
||||||
case UNICODE_MULTIPLICATION_SIGN:
|
|
||||||
case UNICODE_PI:
|
|
||||||
case UNICODE_PILCROW_SIGN:
|
|
||||||
case UNICODE_POUND_SIGN:
|
|
||||||
case UNICODE_REGISTERED_SIGN:
|
|
||||||
case UNICODE_SQUARE_ROOT:
|
|
||||||
case UNICODE_TRADEMARK_SIGN:
|
|
||||||
case UNICODE_WHITE_BULLET:
|
|
||||||
case UNICODE_YEN_SIGN:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Look up the character's key events in KeyCharacterMap below.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mKeyCharacterMap == null) {
|
|
||||||
mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
|
|
||||||
}
|
|
||||||
|
|
||||||
char[] inputChars = { inputChar };
|
|
||||||
return mKeyCharacterMap.getEvents(inputChars);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static KeyEvent[] createKeyDownKeyUpEvents(int keyCode, int metaState) {
|
|
||||||
long now = SystemClock.uptimeMillis();
|
|
||||||
KeyEvent keyDown = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, metaState);
|
|
||||||
KeyEvent keyUp = KeyEvent.changeAction(keyDown, KeyEvent.ACTION_UP);
|
|
||||||
KeyEvent[] events = { keyDown, keyUp };
|
|
||||||
return events;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void endComposition() {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(LOGTAG, "IME: endComposition: IME_COMPOSITION_END");
|
|
||||||
GeckoApp.assertOnUiThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasCompositionString())
|
|
||||||
Log.e(LOGTAG, "Please report this bug:",
|
|
||||||
new IllegalStateException("endComposition, but not composing text?!"));
|
|
||||||
|
|
||||||
GeckoAppShell.sendEventToGecko(
|
|
||||||
GeckoEvent.createIMEEvent(GeckoEvent.IME_COMPOSITION_END, 0, 0));
|
|
||||||
|
|
||||||
mCompositionStart = NO_COMPOSITION_STRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendTextToGecko(CharSequence text, int caretPos) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(LOGTAG, "IME: sendTextToGecko(\"" + text + "\")");
|
|
||||||
GeckoApp.assertOnUiThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle composition text styles
|
|
||||||
if (text != null && text instanceof Spanned) {
|
|
||||||
Spanned span = (Spanned) text;
|
|
||||||
int spanStart = 0, spanEnd = 0;
|
|
||||||
boolean pastSelStart = false, pastSelEnd = false;
|
|
||||||
|
|
||||||
do {
|
|
||||||
int rangeType = GeckoEvent.IME_RANGE_CONVERTEDTEXT;
|
|
||||||
int rangeStyles = 0, rangeForeColor = 0, rangeBackColor = 0;
|
|
||||||
|
|
||||||
// Find next offset where there is a style transition
|
|
||||||
spanEnd = span.nextSpanTransition(spanStart + 1, text.length(),
|
|
||||||
CharacterStyle.class);
|
|
||||||
|
|
||||||
// Empty range, continue
|
|
||||||
if (spanEnd <= spanStart)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Get and iterate through list of span objects within range
|
|
||||||
CharacterStyle[] styles = span.getSpans(spanStart, spanEnd, CharacterStyle.class);
|
|
||||||
|
|
||||||
for (CharacterStyle style : styles) {
|
|
||||||
if (style instanceof UnderlineSpan) {
|
|
||||||
// Text should be underlined
|
|
||||||
rangeStyles |= GeckoEvent.IME_RANGE_UNDERLINE;
|
|
||||||
} else if (style instanceof ForegroundColorSpan) {
|
|
||||||
// Text should be of a different foreground color
|
|
||||||
rangeStyles |= GeckoEvent.IME_RANGE_FORECOLOR;
|
|
||||||
rangeForeColor = ((ForegroundColorSpan) style).getForegroundColor();
|
|
||||||
} else if (style instanceof BackgroundColorSpan) {
|
|
||||||
// Text should be of a different background color
|
|
||||||
rangeStyles |= GeckoEvent.IME_RANGE_BACKCOLOR;
|
|
||||||
rangeBackColor = ((BackgroundColorSpan) style).getBackgroundColor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add range to array, the actual styles are
|
|
||||||
// applied when IME_SET_TEXT is sent
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(LOGTAG, String.format(
|
|
||||||
". . . sendTextToGecko: IME_ADD_RANGE, %d, %d, %d, %d, %d, %d",
|
|
||||||
spanStart, spanEnd - spanStart, rangeType, rangeStyles, rangeForeColor,
|
|
||||||
rangeBackColor));
|
|
||||||
}
|
|
||||||
|
|
||||||
GeckoAppShell.sendEventToGecko(
|
|
||||||
GeckoEvent.createIMERangeEvent(spanStart, spanEnd - spanStart,
|
|
||||||
rangeType, rangeStyles,
|
|
||||||
rangeForeColor, rangeBackColor));
|
|
||||||
|
|
||||||
spanStart = spanEnd;
|
|
||||||
} while (spanStart < text.length());
|
|
||||||
} else {
|
|
||||||
if (DEBUG) Log.d(LOGTAG, ". . . sendTextToGecko: IME_ADD_RANGE, 0, " + text.length()
|
|
||||||
+ ", IME_RANGE_RAWINPUT, IME_RANGE_UNDERLINE)");
|
|
||||||
GeckoAppShell.sendEventToGecko(
|
|
||||||
GeckoEvent.createIMERangeEvent(0, text == null ? 0 : text.length(),
|
|
||||||
GeckoEvent.IME_RANGE_RAWINPUT,
|
|
||||||
GeckoEvent.IME_RANGE_UNDERLINE, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change composition (treating selection end as where the caret is)
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(LOGTAG, ". . . sendTextToGecko: IME_SET_TEXT, IME_RANGE_CARETPOSITION, \""
|
|
||||||
+ text + "\")");
|
|
||||||
}
|
|
||||||
|
|
||||||
GeckoAppShell.sendEventToGecko(
|
|
||||||
GeckoEvent.createIMERangeEvent(caretPos, 0,
|
|
||||||
GeckoEvent.IME_RANGE_CARETPOSITION, 0, 0, 0,
|
|
||||||
text.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void afterTextChanged(Editable s) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||||
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
|
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
|
||||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
|
outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
|
||||||
|
@ -1016,57 +706,53 @@ class GeckoInputConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyIME(final int type, final int state) {
|
public void notifyIME(final int type, final int state) {
|
||||||
postToUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
View v = getView();
|
|
||||||
if (v == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (type) {
|
final View v = getView();
|
||||||
case NOTIFY_IME_RESETINPUTSTATE:
|
if (v == null)
|
||||||
if (DEBUG) Log.d(LOGTAG, ". . . notifyIME: reset");
|
return;
|
||||||
|
|
||||||
// Gecko just cancelled the current composition from underneath us,
|
switch (type) {
|
||||||
// so abandon our active composition string WITHOUT committing it!
|
case NOTIFY_IME_RESETINPUTSTATE:
|
||||||
resetCompositionState();
|
if (DEBUG) Log.d(LOGTAG, ". . . notifyIME: reset");
|
||||||
|
|
||||||
// Don't use IMEStateUpdater for reset.
|
resetCompositionState();
|
||||||
// Because IME may not work showSoftInput()
|
|
||||||
// after calling restartInput() immediately.
|
|
||||||
// So we have to call showSoftInput() delay.
|
|
||||||
InputMethodManager imm = getInputMethodManager();
|
|
||||||
if (imm == null) {
|
|
||||||
// no way to reset IME status directly
|
|
||||||
IMEStateUpdater.resetIME();
|
|
||||||
} else {
|
|
||||||
imm.restartInput(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep current enabled state
|
// Don't use IMEStateUpdater for reset.
|
||||||
IMEStateUpdater.enableIME();
|
// Because IME may not work showSoftInput()
|
||||||
break;
|
// after calling restartInput() immediately.
|
||||||
|
// So we have to call showSoftInput() delay.
|
||||||
case NOTIFY_IME_CANCELCOMPOSITION:
|
InputMethodManager imm = getInputMethodManager();
|
||||||
if (DEBUG) Log.d(LOGTAG, ". . . notifyIME: cancel");
|
if (imm == null) {
|
||||||
IMEStateUpdater.resetIME();
|
// no way to reset IME status directly
|
||||||
break;
|
IMEStateUpdater.resetIME();
|
||||||
|
} else {
|
||||||
case NOTIFY_IME_FOCUSCHANGE:
|
imm.restartInput(v);
|
||||||
if (DEBUG) Log.d(LOGTAG, ". . . notifyIME: focus");
|
|
||||||
IMEStateUpdater.resetIME();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NOTIFY_IME_SETOPENSTATE:
|
|
||||||
default:
|
|
||||||
if (DEBUG)
|
|
||||||
throw new IllegalArgumentException("Unexpected NOTIFY_IME=" + type);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
// keep current enabled state
|
||||||
|
IMEStateUpdater.enableIME();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NOTIFY_IME_CANCELCOMPOSITION:
|
||||||
|
if (DEBUG) Log.d(LOGTAG, ". . . notifyIME: cancel");
|
||||||
|
removeComposingSpans(getEditable());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NOTIFY_IME_FOCUSCHANGE:
|
||||||
|
if (DEBUG) Log.d(LOGTAG, ". . . notifyIME: focus");
|
||||||
|
IMEStateUpdater.resetIME();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (DEBUG) {
|
||||||
|
throw new IllegalArgumentException("Unexpected NOTIFY_IME=" + type);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyIMEEnabled(final int state, final String typeHint, final String modeHint, final String actionHint) {
|
public void notifyIMEEnabled(final int state, final String typeHint,
|
||||||
|
final String modeHint, final String actionHint) {
|
||||||
// For some input type we will use a widget to display the ui, for those we must not
|
// For some input type we will use a widget to display the ui, for those we must not
|
||||||
// display the ime. We can display a widget for date and time types and, if the sdk version
|
// display the ime. We can display a widget for date and time types and, if the sdk version
|
||||||
// is greater than 11, for datetime/month/week as well.
|
// is greater than 11, for datetime/month/week as well.
|
||||||
|
@ -1077,41 +763,17 @@ class GeckoInputConnection
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
postToUiThread(new Runnable() {
|
final View v = getView();
|
||||||
public void run() {
|
if (v == null)
|
||||||
View v = getView();
|
return;
|
||||||
if (v == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* When IME is 'disabled', IME processing is disabled.
|
/* When IME is 'disabled', IME processing is disabled.
|
||||||
In addition, the IME UI is hidden */
|
In addition, the IME UI is hidden */
|
||||||
mIMEState = state;
|
mIMEState = state;
|
||||||
mIMETypeHint = (typeHint == null) ? "" : typeHint;
|
mIMETypeHint = (typeHint == null) ? "" : typeHint;
|
||||||
mIMEModeHint = (modeHint == null) ? "" : modeHint;
|
mIMEModeHint = (modeHint == null) ? "" : modeHint;
|
||||||
mIMEActionHint = (actionHint == null) ? "" : actionHint;
|
mIMEActionHint = (actionHint == null) ? "" : actionHint;
|
||||||
IMEStateUpdater.enableIME();
|
IMEStateUpdater.enableIME();
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void notifyIMEChange(final String text, final int start, final int end,
|
|
||||||
final int newEnd) {
|
|
||||||
if (newEnd < 0) {
|
|
||||||
// FIXME: Post notifySelectionChange() to UI thread after bug 780543 is fixed.
|
|
||||||
// notifyIMEChange() is called on the Gecko thread. We want to run all
|
|
||||||
// InputMethodManager code on the UI thread to avoid IME race conditions that cause
|
|
||||||
// crashes like bug 747629. However, if notifySelectionChange() is run on the UI thread,
|
|
||||||
// it causes mysterious problems with repeating characters like bug 780543. This
|
|
||||||
// band-aid fix is to run all InputMethodManager code on the UI thread except
|
|
||||||
// notifySelectionChange() until I can find the root cause.
|
|
||||||
notifySelectionChange(start, end);
|
|
||||||
} else {
|
|
||||||
postToUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
notifyTextChange(text, start, end, newEnd);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delay updating IME states (see bug 573800) */
|
/* Delay updating IME states (see bug 573800) */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче