This CL demonstrates how the new language switching functionality should work by using the SoftKeyboard sample. BUG: 15267645 Change-Id: I18ab25a0784979fe6028b97a22ff02bfd502d506
721 lines
27 KiB
Java
721 lines
27 KiB
Java
/*
|
|
* Copyright (C) 2008-2009 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.example.android.softkeyboard;
|
|
|
|
import android.app.Dialog;
|
|
import android.inputmethodservice.InputMethodService;
|
|
import android.inputmethodservice.Keyboard;
|
|
import android.inputmethodservice.KeyboardView;
|
|
import android.os.IBinder;
|
|
import android.text.InputType;
|
|
import android.text.method.MetaKeyKeyListener;
|
|
import android.view.KeyCharacterMap;
|
|
import android.view.KeyEvent;
|
|
import android.view.View;
|
|
import android.view.Window;
|
|
import android.view.inputmethod.CompletionInfo;
|
|
import android.view.inputmethod.EditorInfo;
|
|
import android.view.inputmethod.InputConnection;
|
|
import android.view.inputmethod.InputMethodManager;
|
|
import android.view.inputmethod.InputMethodSubtype;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Example of writing an input method for a soft keyboard. This code is
|
|
* focused on simplicity over completeness, so it should in no way be considered
|
|
* to be a complete soft keyboard implementation. Its purpose is to provide
|
|
* a basic example for how you would get started writing an input method, to
|
|
* be fleshed out as appropriate.
|
|
*/
|
|
public class SoftKeyboard extends InputMethodService
|
|
implements KeyboardView.OnKeyboardActionListener {
|
|
static final boolean DEBUG = false;
|
|
|
|
/**
|
|
* This boolean indicates the optional example code for performing
|
|
* processing of hard keys in addition to regular text generation
|
|
* from on-screen interaction. It would be used for input methods that
|
|
* perform language translations (such as converting text entered on
|
|
* a QWERTY keyboard to Chinese), but may not be used for input methods
|
|
* that are primarily intended to be used for on-screen text entry.
|
|
*/
|
|
static final boolean PROCESS_HARD_KEYS = true;
|
|
|
|
private InputMethodManager mInputMethodManager;
|
|
|
|
private LatinKeyboardView mInputView;
|
|
private CandidateView mCandidateView;
|
|
private CompletionInfo[] mCompletions;
|
|
|
|
private StringBuilder mComposing = new StringBuilder();
|
|
private boolean mPredictionOn;
|
|
private boolean mCompletionOn;
|
|
private int mLastDisplayWidth;
|
|
private boolean mCapsLock;
|
|
private long mLastShiftTime;
|
|
private long mMetaState;
|
|
|
|
private LatinKeyboard mSymbolsKeyboard;
|
|
private LatinKeyboard mSymbolsShiftedKeyboard;
|
|
private LatinKeyboard mQwertyKeyboard;
|
|
|
|
private LatinKeyboard mCurKeyboard;
|
|
|
|
private String mWordSeparators;
|
|
|
|
/**
|
|
* Main initialization of the input method component. Be sure to call
|
|
* to super class.
|
|
*/
|
|
@Override public void onCreate() {
|
|
super.onCreate();
|
|
mInputMethodManager = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
|
|
mWordSeparators = getResources().getString(R.string.word_separators);
|
|
}
|
|
|
|
/**
|
|
* This is the point where you can do all of your UI initialization. It
|
|
* is called after creation and any configuration change.
|
|
*/
|
|
@Override public void onInitializeInterface() {
|
|
if (mQwertyKeyboard != null) {
|
|
// Configuration changes can happen after the keyboard gets recreated,
|
|
// so we need to be able to re-build the keyboards if the available
|
|
// space has changed.
|
|
int displayWidth = getMaxWidth();
|
|
if (displayWidth == mLastDisplayWidth) return;
|
|
mLastDisplayWidth = displayWidth;
|
|
}
|
|
mQwertyKeyboard = new LatinKeyboard(this, R.xml.qwerty);
|
|
mSymbolsKeyboard = new LatinKeyboard(this, R.xml.symbols);
|
|
mSymbolsShiftedKeyboard = new LatinKeyboard(this, R.xml.symbols_shift);
|
|
}
|
|
|
|
/**
|
|
* Called by the framework when your view for creating input needs to
|
|
* be generated. This will be called the first time your input method
|
|
* is displayed, and every time it needs to be re-created such as due to
|
|
* a configuration change.
|
|
*/
|
|
@Override public View onCreateInputView() {
|
|
mInputView = (LatinKeyboardView) getLayoutInflater().inflate(
|
|
R.layout.input, null);
|
|
mInputView.setOnKeyboardActionListener(this);
|
|
setLatinKeyboard(mQwertyKeyboard);
|
|
return mInputView;
|
|
}
|
|
|
|
private void setLatinKeyboard(LatinKeyboard nextKeyboard) {
|
|
final boolean shouldSupportLanguageSwitchKey =
|
|
mInputMethodManager.shouldOfferSwitchingToNextInputMethod(getToken());
|
|
nextKeyboard.setLanguageSwitchKeyVisibility(shouldSupportLanguageSwitchKey);
|
|
mInputView.setKeyboard(nextKeyboard);
|
|
}
|
|
|
|
/**
|
|
* Called by the framework when your view for showing candidates needs to
|
|
* be generated, like {@link #onCreateInputView}.
|
|
*/
|
|
@Override public View onCreateCandidatesView() {
|
|
mCandidateView = new CandidateView(this);
|
|
mCandidateView.setService(this);
|
|
return mCandidateView;
|
|
}
|
|
|
|
/**
|
|
* This is the main point where we do our initialization of the input method
|
|
* to begin operating on an application. At this point we have been
|
|
* bound to the client, and are now receiving all of the detailed information
|
|
* about the target of our edits.
|
|
*/
|
|
@Override public void onStartInput(EditorInfo attribute, boolean restarting) {
|
|
super.onStartInput(attribute, restarting);
|
|
|
|
// Reset our state. We want to do this even if restarting, because
|
|
// the underlying state of the text editor could have changed in any way.
|
|
mComposing.setLength(0);
|
|
updateCandidates();
|
|
|
|
if (!restarting) {
|
|
// Clear shift states.
|
|
mMetaState = 0;
|
|
}
|
|
|
|
mPredictionOn = false;
|
|
mCompletionOn = false;
|
|
mCompletions = null;
|
|
|
|
// We are now going to initialize our state based on the type of
|
|
// text being edited.
|
|
switch (attribute.inputType & InputType.TYPE_MASK_CLASS) {
|
|
case InputType.TYPE_CLASS_NUMBER:
|
|
case InputType.TYPE_CLASS_DATETIME:
|
|
// Numbers and dates default to the symbols keyboard, with
|
|
// no extra features.
|
|
mCurKeyboard = mSymbolsKeyboard;
|
|
break;
|
|
|
|
case InputType.TYPE_CLASS_PHONE:
|
|
// Phones will also default to the symbols keyboard, though
|
|
// often you will want to have a dedicated phone keyboard.
|
|
mCurKeyboard = mSymbolsKeyboard;
|
|
break;
|
|
|
|
case InputType.TYPE_CLASS_TEXT:
|
|
// This is general text editing. We will default to the
|
|
// normal alphabetic keyboard, and assume that we should
|
|
// be doing predictive text (showing candidates as the
|
|
// user types).
|
|
mCurKeyboard = mQwertyKeyboard;
|
|
mPredictionOn = true;
|
|
|
|
// We now look for a few special variations of text that will
|
|
// modify our behavior.
|
|
int variation = attribute.inputType & InputType.TYPE_MASK_VARIATION;
|
|
if (variation == InputType.TYPE_TEXT_VARIATION_PASSWORD ||
|
|
variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
|
|
// Do not display predictions / what the user is typing
|
|
// when they are entering a password.
|
|
mPredictionOn = false;
|
|
}
|
|
|
|
if (variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|
|
|| variation == InputType.TYPE_TEXT_VARIATION_URI
|
|
|| variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
|
|
// Our predictions are not useful for e-mail addresses
|
|
// or URIs.
|
|
mPredictionOn = false;
|
|
}
|
|
|
|
if ((attribute.inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
|
|
// If this is an auto-complete text view, then our predictions
|
|
// will not be shown and instead we will allow the editor
|
|
// to supply their own. We only show the editor's
|
|
// candidates when in fullscreen mode, otherwise relying
|
|
// own it displaying its own UI.
|
|
mPredictionOn = false;
|
|
mCompletionOn = isFullscreenMode();
|
|
}
|
|
|
|
// We also want to look at the current state of the editor
|
|
// to decide whether our alphabetic keyboard should start out
|
|
// shifted.
|
|
updateShiftKeyState(attribute);
|
|
break;
|
|
|
|
default:
|
|
// For all unknown input types, default to the alphabetic
|
|
// keyboard with no special features.
|
|
mCurKeyboard = mQwertyKeyboard;
|
|
updateShiftKeyState(attribute);
|
|
}
|
|
|
|
// Update the label on the enter key, depending on what the application
|
|
// says it will do.
|
|
mCurKeyboard.setImeOptions(getResources(), attribute.imeOptions);
|
|
}
|
|
|
|
/**
|
|
* This is called when the user is done editing a field. We can use
|
|
* this to reset our state.
|
|
*/
|
|
@Override public void onFinishInput() {
|
|
super.onFinishInput();
|
|
|
|
// Clear current composing text and candidates.
|
|
mComposing.setLength(0);
|
|
updateCandidates();
|
|
|
|
// We only hide the candidates window when finishing input on
|
|
// a particular editor, to avoid popping the underlying application
|
|
// up and down if the user is entering text into the bottom of
|
|
// its window.
|
|
setCandidatesViewShown(false);
|
|
|
|
mCurKeyboard = mQwertyKeyboard;
|
|
if (mInputView != null) {
|
|
mInputView.closing();
|
|
}
|
|
}
|
|
|
|
@Override public void onStartInputView(EditorInfo attribute, boolean restarting) {
|
|
super.onStartInputView(attribute, restarting);
|
|
// Apply the selected keyboard to the input view.
|
|
setLatinKeyboard(mCurKeyboard);
|
|
mInputView.closing();
|
|
final InputMethodSubtype subtype = mInputMethodManager.getCurrentInputMethodSubtype();
|
|
mInputView.setSubtypeOnSpaceKey(subtype);
|
|
}
|
|
|
|
@Override
|
|
public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
|
|
mInputView.setSubtypeOnSpaceKey(subtype);
|
|
}
|
|
|
|
/**
|
|
* Deal with the editor reporting movement of its cursor.
|
|
*/
|
|
@Override public void onUpdateSelection(int oldSelStart, int oldSelEnd,
|
|
int newSelStart, int newSelEnd,
|
|
int candidatesStart, int candidatesEnd) {
|
|
super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
|
|
candidatesStart, candidatesEnd);
|
|
|
|
// If the current selection in the text view changes, we should
|
|
// clear whatever candidate text we have.
|
|
if (mComposing.length() > 0 && (newSelStart != candidatesEnd
|
|
|| newSelEnd != candidatesEnd)) {
|
|
mComposing.setLength(0);
|
|
updateCandidates();
|
|
InputConnection ic = getCurrentInputConnection();
|
|
if (ic != null) {
|
|
ic.finishComposingText();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This tells us about completions that the editor has determined based
|
|
* on the current text in it. We want to use this in fullscreen mode
|
|
* to show the completions ourself, since the editor can not be seen
|
|
* in that situation.
|
|
*/
|
|
@Override public void onDisplayCompletions(CompletionInfo[] completions) {
|
|
if (mCompletionOn) {
|
|
mCompletions = completions;
|
|
if (completions == null) {
|
|
setSuggestions(null, false, false);
|
|
return;
|
|
}
|
|
|
|
List<String> stringList = new ArrayList<String>();
|
|
for (int i = 0; i < completions.length; i++) {
|
|
CompletionInfo ci = completions[i];
|
|
if (ci != null) stringList.add(ci.getText().toString());
|
|
}
|
|
setSuggestions(stringList, true, true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This translates incoming hard key events in to edit operations on an
|
|
* InputConnection. It is only needed when using the
|
|
* PROCESS_HARD_KEYS option.
|
|
*/
|
|
private boolean translateKeyDown(int keyCode, KeyEvent event) {
|
|
mMetaState = MetaKeyKeyListener.handleKeyDown(mMetaState,
|
|
keyCode, event);
|
|
int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(mMetaState));
|
|
mMetaState = MetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState);
|
|
InputConnection ic = getCurrentInputConnection();
|
|
if (c == 0 || ic == null) {
|
|
return false;
|
|
}
|
|
|
|
boolean dead = false;
|
|
|
|
if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) {
|
|
dead = true;
|
|
c = c & KeyCharacterMap.COMBINING_ACCENT_MASK;
|
|
}
|
|
|
|
if (mComposing.length() > 0) {
|
|
char accent = mComposing.charAt(mComposing.length() -1 );
|
|
int composed = KeyEvent.getDeadChar(accent, c);
|
|
|
|
if (composed != 0) {
|
|
c = composed;
|
|
mComposing.setLength(mComposing.length()-1);
|
|
}
|
|
}
|
|
|
|
onKey(c, null);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Use this to monitor key events being delivered to the application.
|
|
* We get first crack at them, and can either resume them or let them
|
|
* continue to the app.
|
|
*/
|
|
@Override public boolean onKeyDown(int keyCode, KeyEvent event) {
|
|
switch (keyCode) {
|
|
case KeyEvent.KEYCODE_BACK:
|
|
// The InputMethodService already takes care of the back
|
|
// key for us, to dismiss the input method if it is shown.
|
|
// However, our keyboard could be showing a pop-up window
|
|
// that back should dismiss, so we first allow it to do that.
|
|
if (event.getRepeatCount() == 0 && mInputView != null) {
|
|
if (mInputView.handleBack()) {
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case KeyEvent.KEYCODE_DEL:
|
|
// Special handling of the delete key: if we currently are
|
|
// composing text for the user, we want to modify that instead
|
|
// of let the application to the delete itself.
|
|
if (mComposing.length() > 0) {
|
|
onKey(Keyboard.KEYCODE_DELETE, null);
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
case KeyEvent.KEYCODE_ENTER:
|
|
// Let the underlying text editor always handle these.
|
|
return false;
|
|
|
|
default:
|
|
// For all other keys, if we want to do transformations on
|
|
// text being entered with a hard keyboard, we need to process
|
|
// it and do the appropriate action.
|
|
if (PROCESS_HARD_KEYS) {
|
|
if (keyCode == KeyEvent.KEYCODE_SPACE
|
|
&& (event.getMetaState()&KeyEvent.META_ALT_ON) != 0) {
|
|
// A silly example: in our input method, Alt+Space
|
|
// is a shortcut for 'android' in lower case.
|
|
InputConnection ic = getCurrentInputConnection();
|
|
if (ic != null) {
|
|
// First, tell the editor that it is no longer in the
|
|
// shift state, since we are consuming this.
|
|
ic.clearMetaKeyStates(KeyEvent.META_ALT_ON);
|
|
keyDownUp(KeyEvent.KEYCODE_A);
|
|
keyDownUp(KeyEvent.KEYCODE_N);
|
|
keyDownUp(KeyEvent.KEYCODE_D);
|
|
keyDownUp(KeyEvent.KEYCODE_R);
|
|
keyDownUp(KeyEvent.KEYCODE_O);
|
|
keyDownUp(KeyEvent.KEYCODE_I);
|
|
keyDownUp(KeyEvent.KEYCODE_D);
|
|
// And we consume this event.
|
|
return true;
|
|
}
|
|
}
|
|
if (mPredictionOn && translateKeyDown(keyCode, event)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return super.onKeyDown(keyCode, event);
|
|
}
|
|
|
|
/**
|
|
* Use this to monitor key events being delivered to the application.
|
|
* We get first crack at them, and can either resume them or let them
|
|
* continue to the app.
|
|
*/
|
|
@Override public boolean onKeyUp(int keyCode, KeyEvent event) {
|
|
// If we want to do transformations on text being entered with a hard
|
|
// keyboard, we need to process the up events to update the meta key
|
|
// state we are tracking.
|
|
if (PROCESS_HARD_KEYS) {
|
|
if (mPredictionOn) {
|
|
mMetaState = MetaKeyKeyListener.handleKeyUp(mMetaState,
|
|
keyCode, event);
|
|
}
|
|
}
|
|
|
|
return super.onKeyUp(keyCode, event);
|
|
}
|
|
|
|
/**
|
|
* Helper function to commit any text being composed in to the editor.
|
|
*/
|
|
private void commitTyped(InputConnection inputConnection) {
|
|
if (mComposing.length() > 0) {
|
|
inputConnection.commitText(mComposing, mComposing.length());
|
|
mComposing.setLength(0);
|
|
updateCandidates();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper to update the shift state of our keyboard based on the initial
|
|
* editor state.
|
|
*/
|
|
private void updateShiftKeyState(EditorInfo attr) {
|
|
if (attr != null
|
|
&& mInputView != null && mQwertyKeyboard == mInputView.getKeyboard()) {
|
|
int caps = 0;
|
|
EditorInfo ei = getCurrentInputEditorInfo();
|
|
if (ei != null && ei.inputType != InputType.TYPE_NULL) {
|
|
caps = getCurrentInputConnection().getCursorCapsMode(attr.inputType);
|
|
}
|
|
mInputView.setShifted(mCapsLock || caps != 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper to determine if a given character code is alphabetic.
|
|
*/
|
|
private boolean isAlphabet(int code) {
|
|
if (Character.isLetter(code)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper to send a key down / key up pair to the current editor.
|
|
*/
|
|
private void keyDownUp(int keyEventCode) {
|
|
getCurrentInputConnection().sendKeyEvent(
|
|
new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode));
|
|
getCurrentInputConnection().sendKeyEvent(
|
|
new KeyEvent(KeyEvent.ACTION_UP, keyEventCode));
|
|
}
|
|
|
|
/**
|
|
* Helper to send a character to the editor as raw key events.
|
|
*/
|
|
private void sendKey(int keyCode) {
|
|
switch (keyCode) {
|
|
case '\n':
|
|
keyDownUp(KeyEvent.KEYCODE_ENTER);
|
|
break;
|
|
default:
|
|
if (keyCode >= '0' && keyCode <= '9') {
|
|
keyDownUp(keyCode - '0' + KeyEvent.KEYCODE_0);
|
|
} else {
|
|
getCurrentInputConnection().commitText(String.valueOf((char) keyCode), 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Implementation of KeyboardViewListener
|
|
|
|
public void onKey(int primaryCode, int[] keyCodes) {
|
|
if (isWordSeparator(primaryCode)) {
|
|
// Handle separator
|
|
if (mComposing.length() > 0) {
|
|
commitTyped(getCurrentInputConnection());
|
|
}
|
|
sendKey(primaryCode);
|
|
updateShiftKeyState(getCurrentInputEditorInfo());
|
|
} else if (primaryCode == Keyboard.KEYCODE_DELETE) {
|
|
handleBackspace();
|
|
} else if (primaryCode == Keyboard.KEYCODE_SHIFT) {
|
|
handleShift();
|
|
} else if (primaryCode == Keyboard.KEYCODE_CANCEL) {
|
|
handleClose();
|
|
return;
|
|
} else if (primaryCode == LatinKeyboardView.KEYCODE_LANGUAGE_SWITCH) {
|
|
handleLanguageSwitch();
|
|
return;
|
|
} else if (primaryCode == LatinKeyboardView.KEYCODE_OPTIONS) {
|
|
// Show a menu or somethin'
|
|
} else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE
|
|
&& mInputView != null) {
|
|
Keyboard current = mInputView.getKeyboard();
|
|
if (current == mSymbolsKeyboard || current == mSymbolsShiftedKeyboard) {
|
|
setLatinKeyboard(mQwertyKeyboard);
|
|
} else {
|
|
setLatinKeyboard(mSymbolsKeyboard);
|
|
mSymbolsKeyboard.setShifted(false);
|
|
}
|
|
} else {
|
|
handleCharacter(primaryCode, keyCodes);
|
|
}
|
|
}
|
|
|
|
public void onText(CharSequence text) {
|
|
InputConnection ic = getCurrentInputConnection();
|
|
if (ic == null) return;
|
|
ic.beginBatchEdit();
|
|
if (mComposing.length() > 0) {
|
|
commitTyped(ic);
|
|
}
|
|
ic.commitText(text, 0);
|
|
ic.endBatchEdit();
|
|
updateShiftKeyState(getCurrentInputEditorInfo());
|
|
}
|
|
|
|
/**
|
|
* Update the list of available candidates from the current composing
|
|
* text. This will need to be filled in by however you are determining
|
|
* candidates.
|
|
*/
|
|
private void updateCandidates() {
|
|
if (!mCompletionOn) {
|
|
if (mComposing.length() > 0) {
|
|
ArrayList<String> list = new ArrayList<String>();
|
|
list.add(mComposing.toString());
|
|
setSuggestions(list, true, true);
|
|
} else {
|
|
setSuggestions(null, false, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setSuggestions(List<String> suggestions, boolean completions,
|
|
boolean typedWordValid) {
|
|
if (suggestions != null && suggestions.size() > 0) {
|
|
setCandidatesViewShown(true);
|
|
} else if (isExtractViewShown()) {
|
|
setCandidatesViewShown(true);
|
|
}
|
|
if (mCandidateView != null) {
|
|
mCandidateView.setSuggestions(suggestions, completions, typedWordValid);
|
|
}
|
|
}
|
|
|
|
private void handleBackspace() {
|
|
final int length = mComposing.length();
|
|
if (length > 1) {
|
|
mComposing.delete(length - 1, length);
|
|
getCurrentInputConnection().setComposingText(mComposing, 1);
|
|
updateCandidates();
|
|
} else if (length > 0) {
|
|
mComposing.setLength(0);
|
|
getCurrentInputConnection().commitText("", 0);
|
|
updateCandidates();
|
|
} else {
|
|
keyDownUp(KeyEvent.KEYCODE_DEL);
|
|
}
|
|
updateShiftKeyState(getCurrentInputEditorInfo());
|
|
}
|
|
|
|
private void handleShift() {
|
|
if (mInputView == null) {
|
|
return;
|
|
}
|
|
|
|
Keyboard currentKeyboard = mInputView.getKeyboard();
|
|
if (mQwertyKeyboard == currentKeyboard) {
|
|
// Alphabet keyboard
|
|
checkToggleCapsLock();
|
|
mInputView.setShifted(mCapsLock || !mInputView.isShifted());
|
|
} else if (currentKeyboard == mSymbolsKeyboard) {
|
|
mSymbolsKeyboard.setShifted(true);
|
|
setLatinKeyboard(mSymbolsShiftedKeyboard);
|
|
mSymbolsShiftedKeyboard.setShifted(true);
|
|
} else if (currentKeyboard == mSymbolsShiftedKeyboard) {
|
|
mSymbolsShiftedKeyboard.setShifted(false);
|
|
setLatinKeyboard(mSymbolsKeyboard);
|
|
mSymbolsKeyboard.setShifted(false);
|
|
}
|
|
}
|
|
|
|
private void handleCharacter(int primaryCode, int[] keyCodes) {
|
|
if (isInputViewShown()) {
|
|
if (mInputView.isShifted()) {
|
|
primaryCode = Character.toUpperCase(primaryCode);
|
|
}
|
|
}
|
|
if (isAlphabet(primaryCode) && mPredictionOn) {
|
|
mComposing.append((char) primaryCode);
|
|
getCurrentInputConnection().setComposingText(mComposing, 1);
|
|
updateShiftKeyState(getCurrentInputEditorInfo());
|
|
updateCandidates();
|
|
} else {
|
|
getCurrentInputConnection().commitText(
|
|
String.valueOf((char) primaryCode), 1);
|
|
}
|
|
}
|
|
|
|
private void handleClose() {
|
|
commitTyped(getCurrentInputConnection());
|
|
requestHideSelf(0);
|
|
mInputView.closing();
|
|
}
|
|
|
|
private IBinder getToken() {
|
|
final Dialog dialog = getWindow();
|
|
if (dialog == null) {
|
|
return null;
|
|
}
|
|
final Window window = dialog.getWindow();
|
|
if (window == null) {
|
|
return null;
|
|
}
|
|
return window.getAttributes().token;
|
|
}
|
|
|
|
private void handleLanguageSwitch() {
|
|
mInputMethodManager.switchToNextInputMethod(getToken(), false /* onlyCurrentIme */);
|
|
}
|
|
|
|
private void checkToggleCapsLock() {
|
|
long now = System.currentTimeMillis();
|
|
if (mLastShiftTime + 800 > now) {
|
|
mCapsLock = !mCapsLock;
|
|
mLastShiftTime = 0;
|
|
} else {
|
|
mLastShiftTime = now;
|
|
}
|
|
}
|
|
|
|
private String getWordSeparators() {
|
|
return mWordSeparators;
|
|
}
|
|
|
|
public boolean isWordSeparator(int code) {
|
|
String separators = getWordSeparators();
|
|
return separators.contains(String.valueOf((char)code));
|
|
}
|
|
|
|
public void pickDefaultCandidate() {
|
|
pickSuggestionManually(0);
|
|
}
|
|
|
|
public void pickSuggestionManually(int index) {
|
|
if (mCompletionOn && mCompletions != null && index >= 0
|
|
&& index < mCompletions.length) {
|
|
CompletionInfo ci = mCompletions[index];
|
|
getCurrentInputConnection().commitCompletion(ci);
|
|
if (mCandidateView != null) {
|
|
mCandidateView.clear();
|
|
}
|
|
updateShiftKeyState(getCurrentInputEditorInfo());
|
|
} else if (mComposing.length() > 0) {
|
|
// If we were generating candidate suggestions for the current
|
|
// text, we would commit one of them here. But for this sample,
|
|
// we will just commit the current text.
|
|
commitTyped(getCurrentInputConnection());
|
|
}
|
|
}
|
|
|
|
public void swipeRight() {
|
|
if (mCompletionOn) {
|
|
pickDefaultCandidate();
|
|
}
|
|
}
|
|
|
|
public void swipeLeft() {
|
|
handleBackspace();
|
|
}
|
|
|
|
public void swipeDown() {
|
|
handleClose();
|
|
}
|
|
|
|
public void swipeUp() {
|
|
}
|
|
|
|
public void onPress(int primaryCode) {
|
|
}
|
|
|
|
public void onRelease(int primaryCode) {
|
|
}
|
|
}
|