下面列出了android.view.KeyEvent#KEYCODE_UNKNOWN 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Override
public boolean onKeyOther(TextView widget, Spannable text, KeyEvent event) {
final int movementMetaState = getMovementMetaState(text, event);
final int keyCode = event.getKeyCode();
if (keyCode != KeyEvent.KEYCODE_UNKNOWN
&& event.getAction() == KeyEvent.ACTION_MULTIPLE) {
final int repeat = event.getRepeatCount();
boolean handled = false;
for (int i = 0; i < repeat; i++) {
if (!handleMovementKey(widget, text, keyCode, movementMetaState, event)) {
break;
}
handled = true;
}
if (handled) {
MetaKeyKeyListener.adjustMetaAfterKeypress(text);
MetaKeyKeyListener.resetLockedMeta(text);
}
return handled;
}
return false;
}
/**
* Base implementation handles ACTION_MULTIPLE KEYCODE_UNKNOWN by inserting
* the event's text into the content.
*/
public boolean onKeyOther(View view, Editable content, KeyEvent event) {
if (event.getAction() != KeyEvent.ACTION_MULTIPLE
|| event.getKeyCode() != KeyEvent.KEYCODE_UNKNOWN) {
// Not something we are interested in.
return false;
}
int selectionStart = Selection.getSelectionStart(content);
int selectionEnd = Selection.getSelectionEnd(content);
if (selectionEnd < selectionStart) {
int temp = selectionEnd;
selectionEnd = selectionStart;
selectionStart = temp;
}
CharSequence text = event.getCharacters();
if (text == null) {
return false;
}
content.replace(selectionStart, selectionEnd, text);
return true;
}
/**
* Code taken from newer version of the support library located in PlaybackStateCompat.toKeyCode
* Replace this to PlaybackStateCompat.toKeyCode when React Native updates the support library
*/
private int toKeyCode(long action) {
if (action == PlaybackStateCompat.ACTION_PLAY) {
return KeyEvent.KEYCODE_MEDIA_PLAY;
} else if (action == PlaybackStateCompat.ACTION_PAUSE) {
return KeyEvent.KEYCODE_MEDIA_PAUSE;
} else if (action == PlaybackStateCompat.ACTION_SKIP_TO_NEXT) {
return KeyEvent.KEYCODE_MEDIA_NEXT;
} else if (action == PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) {
return KeyEvent.KEYCODE_MEDIA_PREVIOUS;
} else if (action == PlaybackStateCompat.ACTION_STOP) {
return KeyEvent.KEYCODE_MEDIA_STOP;
} else if (action == PlaybackStateCompat.ACTION_FAST_FORWARD) {
return KeyEvent.KEYCODE_MEDIA_FAST_FORWARD;
} else if (action == PlaybackStateCompat.ACTION_REWIND) {
return KeyEvent.KEYCODE_MEDIA_REWIND;
} else if (action == PlaybackStateCompat.ACTION_PLAY_PAUSE) {
return KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
}
return KeyEvent.KEYCODE_UNKNOWN;
}
/**
* Constructs an intent from the given information and the search dialog state.
*
* @param action Intent action.
* @param data Intent data, or <code>null</code>.
* @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>.
* @param query Intent query, or <code>null</code>.
* @param actionKey The key code of the action key that was pressed,
* or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
* @param actionMsg The message for the action key that was pressed,
* or <code>null</code> if none.
* @param mode The search mode, one of the acceptable values for
* {@link SearchManager#SEARCH_MODE}, or {@code null}.
* @return The intent.
*/
private Intent createIntent(String action, Uri data, String extraData, String query,
int actionKey, String actionMsg) {
// Now build the Intent
Intent intent = new Intent(action);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// We need CLEAR_TOP to avoid reusing an old task that has other activities
// on top of the one we want. We don't want to do this in in-app search though,
// as it can be destructive to the activity stack.
if (data != null) {
intent.setData(data);
}
intent.putExtra(SearchManager.USER_QUERY, mUserQuery);
if (query != null) {
intent.putExtra(SearchManager.QUERY, query);
}
if (extraData != null) {
intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
}
if (mAppSearchData != null) {
intent.putExtra(SearchManager.APP_DATA, mAppSearchData);
}
if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
intent.putExtra(SearchManager.ACTION_KEY, actionKey);
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
}
intent.setComponent(mSearchable.getSearchActivity());
return intent;
}
/**
* Constructs an intent from the given information and the search dialog state.
*
* @param action Intent action.
* @param data Intent data, or <code>null</code>.
* @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>.
* @param query Intent query, or <code>null</code>.
* @param actionKey The key code of the action key that was pressed,
* or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
* @param actionMsg The message for the action key that was pressed,
* or <code>null</code> if none.
* @return The intent.
*/
private Intent createIntent(String action, Uri data, String extraData, String query,
int actionKey, String actionMsg) {
// Now build the Intent
Intent intent = new Intent(action);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// We need CLEAR_TOP to avoid reusing an old task that has other activities
// on top of the one we want. We don't want to do this in in-app search though,
// as it can be destructive to the activity stack.
if (data != null) {
intent.setData(data);
}
intent.putExtra(SearchManager.USER_QUERY, mUserQuery);
if (query != null) {
intent.putExtra(SearchManager.QUERY, query);
}
if (extraData != null) {
intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
}
if (mAppSearchData != null) {
intent.putExtra(SearchManager.APP_DATA, mAppSearchData);
}
if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
intent.putExtra(SearchManager.ACTION_KEY, actionKey);
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
}
intent.setComponent(mSearchable.getSearchActivity());
return intent;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (mPopup.onKeyDown(keyCode, event)) {
return true;
}
if (!isPopupShowing()) {
switch(keyCode) {
case KeyEvent.KEYCODE_DPAD_DOWN:
if (event.hasNoModifiers()) {
performValidation();
}
}
}
if (isPopupShowing() && keyCode == KeyEvent.KEYCODE_TAB && event.hasNoModifiers()) {
return true;
}
mLastKeyCode = keyCode;
boolean handled = super.onKeyDown(keyCode, event);
mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN;
if (handled && isPopupShowing()) {
clearListSelection();
}
return handled;
}
public boolean onKeyOther(TextView view, Spannable text, KeyEvent event) {
int code = event.getKeyCode();
if (code != KeyEvent.KEYCODE_UNKNOWN
&& event.getAction() == KeyEvent.ACTION_MULTIPLE) {
int repeat = event.getRepeatCount();
boolean handled = false;
while ((--repeat) > 0) {
handled |= executeDown(view, text, code,event);
}
return handled;
}
return false;
}
/**
* Constructs an intent from the given information and the search dialog state.
*
* @param action Intent action.
* @param data Intent data, or <code>null</code>.
* @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>.
* @param query Intent query, or <code>null</code>.
* @param actionKey The key code of the action key that was pressed,
* or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
* @param actionMsg The message for the action key that was pressed,
* or <code>null</code> if none.
* @return The intent.
*/
private Intent createIntent(String action, Uri data, String extraData, String query,
int actionKey, String actionMsg) {
// Now build the Intent
Intent intent = new Intent(action);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// We need CLEAR_TOP to avoid reusing an old task that has other activities
// on top of the one we want. We don't want to do this in in-app search though,
// as it can be destructive to the activity stack.
if (data != null) {
intent.setData(data);
}
intent.putExtra(SearchManager.USER_QUERY, mUserQuery);
if (query != null) {
intent.putExtra(SearchManager.QUERY, query);
}
if (extraData != null) {
intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
}
if (mAppSearchData != null) {
intent.putExtra(SearchManager.APP_DATA, mAppSearchData);
}
if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
intent.putExtra(SearchManager.ACTION_KEY, actionKey);
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
}
intent.setComponent(mSearchable.getSearchActivity());
return intent;
}
/**
* Not undoable
*
* @param symbolicName key code's symbolic name
* @return op that sends the given code
*/
@Override
public Op keyCodeStr(String symbolicName) {
int code = KeyEvent.keyCodeFromString("KEYCODE_" + symbolicName);
if (code != KeyEvent.KEYCODE_UNKNOWN) {
return keyCode(code);
}
return null;
}
@Override
public boolean isEligibleKeyComboCode(long keyComboCode) {
if (keyComboCode == KEY_COMBO_CODE_UNASSIGNED) {
return true;
}
// Do not allow to set key combo which is consisted only with modifiers.
int keyCode = KeyComboManager.getKeyCode(keyComboCode);
if (KeyEvent.isModifierKey(keyCode) || keyCode == KeyEvent.KEYCODE_UNKNOWN) {
return false;
}
// It's not allowed to use trigger modifier as part of key combo code.
return (KeyComboManager.getModifier(keyComboCode) & getTriggerModifier()) == 0;
}
/**
* Intended to mimic the behavior of onKeyEvent if this were the only service running. It will be
* called from onKeyEvent, both from this service and from others in this apk (TalkBack). This
* method must not block, since it will block onKeyEvent as well.
*
* @param keyEvent A key event
* @return {@code true} if the event is handled, {@code false} otherwise.
*/
@Override
public boolean onKeyEventShared(KeyEvent keyEvent) {
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) {
// Tapping on fingerprint sensor somehow files KeyEvent with KEYCODE_UNKNOW, which will change
// input mode to keyboard, and cancel pending accessibility hints. It is OK to just ignore
// these KeyEvents since they're unused in TalkBack.
return false;
}
Performance perf = Performance.getInstance();
EventId eventId = perf.onEventReceived(keyEvent);
if (isServiceActive()) {
// Stop the TTS engine when any key (except for volume up/down key) is pressed on physical
// keyboard.
if (shouldInterruptByAnyKeyEvent()
&& keyEvent.getDeviceId() != 0
&& keyEvent.getAction() == KeyEvent.ACTION_DOWN
&& keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
&& keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP) {
interruptAllFeedback(false /* stopTtsSpeechCompletely */);
}
}
for (ServiceKeyEventListener listener : keyEventListeners) {
if (!isServiceActive() && !listener.processWhenServiceSuspended()) {
continue;
}
if (listener.onKeyEvent(keyEvent, eventId)) {
perf.onHandlerDone(eventId);
return true;
}
}
return false;
}
/**
* Constructs an intent from the given information and the search dialog state.
*
* @param action Intent action.
* @param data Intent data, or <code>null</code>.
* @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>.
* @param query Intent query, or <code>null</code>.
* @param actionKey The key code of the action key that was pressed,
* or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
* @param actionMsg The message for the action key that was pressed,
* or <code>null</code> if none.
* @return The intent.
*/
private Intent createIntent(String action, Uri data, String extraData, String query,
int actionKey, String actionMsg) {
// Now build the Intent
Intent intent = new Intent(action);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// We need CLEAR_TOP to avoid reusing an old task that has other activities
// on top of the one we want. We don't want to do this in in-app search though,
// as it can be destructive to the activity stack.
if (data != null) {
intent.setData(data);
}
intent.putExtra(SearchManager.USER_QUERY, mUserQuery);
if (query != null) {
intent.putExtra(SearchManager.QUERY, query);
}
if (extraData != null) {
intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
}
if (mAppSearchData != null) {
intent.putExtra(SearchManager.APP_DATA, mAppSearchData);
}
if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
intent.putExtra(SearchManager.ACTION_KEY, actionKey);
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
}
intent.setComponent(mSearchable.getSearchActivity());
return intent;
}
protected void updateDisabledButtons(SharedPreferences prefs) {
String value = prefs.getString(
Constants.PREF_DISABLED_BUTTONS, String.valueOf(disabledButtons));
try {
disabledButtons = Integer.parseInt(value, 10);
} catch (NumberFormatException nfe) {
LOGE(TAG, "Error with " + Constants.PREF_DISABLED_BUTTONS + " formatting: " + value);
disabledButtons = KeyEvent.KEYCODE_UNKNOWN;
}
}
public boolean onKeyOther(TextView view, Spannable text, KeyEvent event) {
int code = event.getKeyCode();
if (code != KeyEvent.KEYCODE_UNKNOWN
&& event.getAction() == KeyEvent.ACTION_MULTIPLE) {
int repeat = event.getRepeatCount();
boolean handled = false;
while ((--repeat) > 0) {
handled |= executeDown(view, text, code,event);
}
return handled;
}
return false;
}
protected void updateDisabledButtons(SharedPreferences prefs) {
String value = prefs.getString(
Constants.PREF_DISABLED_BUTTONS, String.valueOf(disabledButtons));
try {
disabledButtons = Integer.parseInt(value, 10);
} catch (NumberFormatException nfe) {
LOGE(TAG, "Error with " + Constants.PREF_DISABLED_BUTTONS + " formatting: " + value);
disabledButtons = KeyEvent.KEYCODE_UNKNOWN;
}
}
public void sendKeyEvent(final KeyEvent keyEvent) {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
// This method is only called for enter or backspace when speaking to old applications
// (target SDK <= 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)), or for digits.
// When talking to new applications we never use this method because it's inherently
// racy and has unpredictable results, but for backward compatibility we continue
// sending the key events for only Enter and Backspace because some applications
// mistakenly catch them to do some stuff.
switch (keyEvent.getKeyCode()) {
case KeyEvent.KEYCODE_ENTER:
mCommittedTextBeforeComposingText.append("\n");
mExpectedSelStart += 1;
mExpectedSelEnd = mExpectedSelStart;
break;
case KeyEvent.KEYCODE_DEL:
if (0 == mComposingText.length()) {
if (mCommittedTextBeforeComposingText.length() > 0) {
mCommittedTextBeforeComposingText.delete(
mCommittedTextBeforeComposingText.length() - 1,
mCommittedTextBeforeComposingText.length());
}
} else {
mComposingText.delete(mComposingText.length() - 1, mComposingText.length());
}
if (mExpectedSelStart > 0 && mExpectedSelStart == mExpectedSelEnd) {
// TODO: Handle surrogate pairs.
mExpectedSelStart -= 1;
}
mExpectedSelEnd = mExpectedSelStart;
break;
case KeyEvent.KEYCODE_UNKNOWN:
if (null != keyEvent.getCharacters()) {
mCommittedTextBeforeComposingText.append(keyEvent.getCharacters());
mExpectedSelStart += keyEvent.getCharacters().length();
mExpectedSelEnd = mExpectedSelStart;
}
break;
default:
final String text = StringUtils.newSingleCodePointString(keyEvent.getUnicodeChar());
mCommittedTextBeforeComposingText.append(text);
mExpectedSelStart += text.length();
mExpectedSelEnd = mExpectedSelStart;
break;
}
}
if (isConnected()) {
mIC.sendKeyEvent(keyEvent);
}
}
public void sendKeyEvent(final KeyEvent keyEvent) {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
// This method is only called for enter or backspace when speaking to old applications
// (target SDK <= 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)), or for digits.
// When talking to new applications we never use this method because it's inherently
// racy and has unpredictable results, but for backward compatibility we continue
// sending the key events for only Enter and Backspace because some applications
// mistakenly catch them to do some stuff.
switch (keyEvent.getKeyCode()) {
case KeyEvent.KEYCODE_ENTER:
mCommittedTextBeforeComposingText.append("\n");
mExpectedSelStart += 1;
mExpectedSelEnd = mExpectedSelStart;
break;
case KeyEvent.KEYCODE_DEL:
if (0 == mComposingText.length()) {
if (mCommittedTextBeforeComposingText.length() > 0) {
mCommittedTextBeforeComposingText.delete(
mCommittedTextBeforeComposingText.length() - 1,
mCommittedTextBeforeComposingText.length());
}
} else {
mComposingText.delete(mComposingText.length() - 1, mComposingText.length());
}
if (mExpectedSelStart > 0 && mExpectedSelStart == mExpectedSelEnd) {
// TODO: Handle surrogate pairs.
mExpectedSelStart -= 1;
}
mExpectedSelEnd = mExpectedSelStart;
break;
case KeyEvent.KEYCODE_UNKNOWN:
if (null != keyEvent.getCharacters()) {
mCommittedTextBeforeComposingText.append(keyEvent.getCharacters());
mExpectedSelStart += keyEvent.getCharacters().length();
mExpectedSelEnd = mExpectedSelStart;
}
break;
default:
final String text = StringUtils.newSingleCodePointString(keyEvent.getUnicodeChar());
mCommittedTextBeforeComposingText.append(text);
mExpectedSelStart += text.length();
mExpectedSelEnd = mExpectedSelStart;
break;
}
}
if (isConnected()) {
mIC.sendKeyEvent(keyEvent);
}
}
public void sendKeyEvent(final KeyEvent keyEvent) {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
// This method is only called for enter or backspace when speaking to old applications
// (target SDK <= 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)), or for digits.
// When talking to new applications we never use this method because it's inherently
// racy and has unpredictable results, but for backward compatibility we continue
// sending the key events for only Enter and Backspace because some applications
// mistakenly catch them to do some stuff.
switch (keyEvent.getKeyCode()) {
case KeyEvent.KEYCODE_ENTER:
mCommittedTextBeforeComposingText.append("\n");
if (hasCursorPosition()) {
mExpectedSelStart += 1;
mExpectedSelEnd = mExpectedSelStart;
}
break;
case KeyEvent.KEYCODE_DEL:
if (0 == mComposingText.length()) {
if (mCommittedTextBeforeComposingText.length() > 0) {
mCommittedTextBeforeComposingText.delete(
mCommittedTextBeforeComposingText.length() - 1,
mCommittedTextBeforeComposingText.length());
}
} else {
mComposingText.delete(mComposingText.length() - 1, mComposingText.length());
}
if (mExpectedSelStart > 0 && mExpectedSelStart == mExpectedSelEnd) {
// TODO: Handle surrogate pairs.
mExpectedSelStart -= 1;
}
mExpectedSelEnd = mExpectedSelStart;
break;
case KeyEvent.KEYCODE_UNKNOWN:
if (null != keyEvent.getCharacters()) {
mCommittedTextBeforeComposingText.append(keyEvent.getCharacters());
if (hasCursorPosition()) {
mExpectedSelStart += keyEvent.getCharacters().length();
mExpectedSelEnd = mExpectedSelStart;
}
}
break;
default:
final String text = StringUtils.newSingleCodePointString(keyEvent.getUnicodeChar());
mCommittedTextBeforeComposingText.append(text);
if (hasCursorPosition()) {
mExpectedSelStart += text.length();
mExpectedSelEnd = mExpectedSelStart;
}
break;
}
}
if (isConnected()) {
mIC.sendKeyEvent(keyEvent);
}
}
public void sendKeyEvent(final KeyEvent keyEvent) {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
// This method is only called for enter or backspace when speaking to old applications
// (target SDK <= 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)), or for digits.
// When talking to new applications we never use this method because it's inherently
// racy and has unpredictable results, but for backward compatibility we continue
// sending the key events for only Enter and Backspace because some applications
// mistakenly catch them to do some stuff.
switch (keyEvent.getKeyCode()) {
case KeyEvent.KEYCODE_ENTER:
mCommittedTextBeforeComposingText.append("\n");
mExpectedSelStart += 1;
mExpectedSelEnd = mExpectedSelStart;
break;
case KeyEvent.KEYCODE_DEL:
if (0 == mComposingText.length()) {
if (mCommittedTextBeforeComposingText.length() > 0) {
mCommittedTextBeforeComposingText.delete(
mCommittedTextBeforeComposingText.length() - 1,
mCommittedTextBeforeComposingText.length());
}
} else {
mComposingText.delete(mComposingText.length() - 1, mComposingText.length());
}
if (mExpectedSelStart > 0 && mExpectedSelStart == mExpectedSelEnd) {
// TODO: Handle surrogate pairs.
mExpectedSelStart -= 1;
}
mExpectedSelEnd = mExpectedSelStart;
break;
case KeyEvent.KEYCODE_UNKNOWN:
if (null != keyEvent.getCharacters()) {
mCommittedTextBeforeComposingText.append(keyEvent.getCharacters());
mExpectedSelStart += keyEvent.getCharacters().length();
mExpectedSelEnd = mExpectedSelStart;
}
break;
default:
final String text = StringUtils.newSingleCodePointString(keyEvent.getUnicodeChar());
mCommittedTextBeforeComposingText.append(text);
mExpectedSelStart += text.length();
mExpectedSelEnd = mExpectedSelStart;
break;
}
}
if (isConnected()) {
mIC.sendKeyEvent(keyEvent);
}
}
public void sendKeyEvent(final KeyEvent keyEvent) {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
// This method is only called for enter or backspace when speaking to old applications
// (target SDK <= 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)), or for digits.
// When talking to new applications we never use this method because it's inherently
// racy and has unpredictable results, but for backward compatibility we continue
// sending the key events for only Enter and Backspace because some applications
// mistakenly catch them to do some stuff.
switch (keyEvent.getKeyCode()) {
case KeyEvent.KEYCODE_ENTER:
mCommittedTextBeforeComposingText.append("\n");
mExpectedSelStart += 1;
mExpectedSelEnd = mExpectedSelStart;
break;
case KeyEvent.KEYCODE_DEL:
if (0 == mComposingText.length()) {
if (mCommittedTextBeforeComposingText.length() > 0) {
mCommittedTextBeforeComposingText.delete(
mCommittedTextBeforeComposingText.length() - 1,
mCommittedTextBeforeComposingText.length());
}
} else {
mComposingText.delete(mComposingText.length() - 1, mComposingText.length());
}
if (mExpectedSelStart > 0 && mExpectedSelStart == mExpectedSelEnd) {
// TODO: Handle surrogate pairs.
mExpectedSelStart -= 1;
}
mExpectedSelEnd = mExpectedSelStart;
break;
case KeyEvent.KEYCODE_UNKNOWN:
if (null != keyEvent.getCharacters()) {
mCommittedTextBeforeComposingText.append(keyEvent.getCharacters());
mExpectedSelStart += keyEvent.getCharacters().length();
mExpectedSelEnd = mExpectedSelStart;
}
break;
default:
final String text = StringUtils.newSingleCodePointString(keyEvent.getUnicodeChar());
mCommittedTextBeforeComposingText.append(text);
mExpectedSelStart += text.length();
mExpectedSelEnd = mExpectedSelStart;
break;
}
}
if (isConnected()) {
mIC.sendKeyEvent(keyEvent);
}
}