diff options
Diffstat (limited to 'java/src')
13 files changed, 395 insertions, 196 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 87de94b76..6473f4558 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -16,6 +16,11 @@ package com.android.inputmethod.latin; +import java.io.InputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.Channels; import java.util.Arrays; import android.content.Context; @@ -27,6 +32,7 @@ import android.util.Log; */ public class BinaryDictionary extends Dictionary { + private static final String TAG = "BinaryDictionary"; public static final int MAX_WORD_LENGTH = 48; private static final int MAX_ALTERNATIVES = 16; private static final int MAX_WORDS = 16; @@ -35,10 +41,13 @@ public class BinaryDictionary extends Dictionary { private static final boolean ENABLE_MISSED_CHARACTERS = true; private int mNativeDict; - private int mDictLength; // This value is set from native code, don't change the name!!!! + private int mDictLength; private int[] mInputCodes = new int[MAX_WORD_LENGTH * MAX_ALTERNATIVES]; private char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS]; private int[] mFrequencies = new int[MAX_WORDS]; + // Keep a reference to the native dict direct buffer in Java to avoid + // unexpected deallocation of the direct buffer. + private ByteBuffer mNativeDictDirectBuffer; static { try { @@ -59,8 +68,27 @@ public class BinaryDictionary extends Dictionary { } } - private native int openNative(AssetManager am, String resourcePath, int typedLetterMultiplier, - int fullWordMultiplier); + /** + * Create a dictionary from a byte buffer. This is used for testing. + * @param context application context for reading resources + * @param resId the resource containing the raw binary dictionary + */ + public BinaryDictionary(Context context, ByteBuffer byteBuffer) { + if (byteBuffer != null) { + if (byteBuffer.isDirect()) { + mNativeDictDirectBuffer = byteBuffer; + } else { + mNativeDictDirectBuffer = ByteBuffer.allocateDirect(byteBuffer.capacity()); + byteBuffer.rewind(); + mNativeDictDirectBuffer.put(byteBuffer); + } + mDictLength = byteBuffer.capacity(); + mNativeDict = openNative(mNativeDictDirectBuffer, + TYPED_LETTER_MULTIPLIER, FULL_WORD_FREQ_MULTIPLIER); + } + } + + private native int openNative(ByteBuffer bb, int typedLetterMultiplier, int fullWordMultiplier); private native void closeNative(int dict); private native boolean isValidWordNative(int nativeData, char[] word, int wordLength); private native int getSuggestionsNative(int dict, int[] inputCodes, int codesSize, @@ -69,9 +97,28 @@ public class BinaryDictionary extends Dictionary { int[] nextLettersFrequencies, int nextLettersSize); private final void loadDictionary(Context context, int resId) { - AssetManager am = context.getResources().getAssets(); - String assetName = context.getResources().getString(resId); - mNativeDict = openNative(am, assetName, TYPED_LETTER_MULTIPLIER, FULL_WORD_FREQ_MULTIPLIER); + InputStream is = context.getResources().openRawResource(resId); + try { + int avail = is.available(); + mNativeDictDirectBuffer = + ByteBuffer.allocateDirect(avail).order(ByteOrder.nativeOrder()); + int got = Channels.newChannel(is).read(mNativeDictDirectBuffer); + if (got != avail) { + Log.e(TAG, "Read " + got + " bytes, expected " + avail); + } else { + mNativeDict = openNative(mNativeDictDirectBuffer, + TYPED_LETTER_MULTIPLIER, FULL_WORD_FREQ_MULTIPLIER); + mDictLength = avail; + } + } catch (IOException e) { + Log.w(TAG, "No available size for binary dictionary"); + } finally { + try { + is.close(); + } catch (IOException e) { + Log.w(TAG, "Failed to close input stream"); + } + } } @Override diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index 3a199bbaf..ae45001b8 100755 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -219,7 +219,7 @@ public class CandidateView extends View { mDivider.getIntrinsicHeight()); } int x = 0; - final int count = mSuggestions.size(); + final int count = Math.min(mSuggestions.size(), MAX_SUGGESTIONS); final int width = getWidth(); final Rect bgPadding = mBgPadding; final Paint paint = mPaint; @@ -335,7 +335,7 @@ public class CandidateView extends View { public void scrollPrev() { int i = 0; - final int count = mSuggestions.size(); + final int count = Math.min(mSuggestions.size(), MAX_SUGGESTIONS); int firstItem = 0; // Actually just before the first item, if at the boundary while (i < count) { if (mWordX[i] < getScrollX() @@ -354,7 +354,7 @@ public class CandidateView extends View { int i = 0; int scrollX = getScrollX(); int targetX = scrollX; - final int count = mSuggestions.size(); + final int count = Math.min(mSuggestions.size(), MAX_SUGGESTIONS); int rightEdge = scrollX + getWidth(); while (i < count) { if (mWordX[i] <= rightEdge && @@ -447,25 +447,6 @@ public class CandidateView extends View { } return true; } - - /** - * For flick through from keyboard, call this method with the x coordinate of the flick - * gesture. - * @param x - */ - public void takeSuggestionAt(float x) { - mTouchX = (int) x; - // To detect candidate - onDraw(null); - if (mSelectedString != null) { - if (!mShowingCompletions) { - TextEntryState.acceptedSuggestion(mSuggestions.get(0), mSelectedString); - } - mService.pickSuggestionManually(mSelectedIndex, mSelectedString); - } - invalidate(); - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_REMOVE_THROUGH_PREVIEW), 200); - } private void hidePreview() { mCurrentWordIndex = OUT_OF_BOUNDS; diff --git a/java/src/com/android/inputmethod/voice/EditingUtil.java b/java/src/com/android/inputmethod/latin/EditingUtil.java index 6316d8ccf..7571f1daf 100644 --- a/java/src/com/android/inputmethod/voice/EditingUtil.java +++ b/java/src/com/android/inputmethod/latin/EditingUtil.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.voice; +package com.android.inputmethod.latin; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; @@ -75,9 +75,21 @@ public class EditingUtil { * represents the cursor, then "hello " will be returned. */ public static String getWordAtCursor( - InputConnection connection, String separators) { - Range range = getWordRangeAtCursor(connection, separators); - return (range == null) ? null : range.word; + InputConnection connection, String separators) { + return getWordAtCursor(connection, separators, null); + } + + /** + * @param connection connection to the current text field. + * @param sep characters which may separate words + * @return the word that surrounds the cursor, including up to one trailing + * separator. For example, if the field contains "he|llo world", where | + * represents the cursor, then "hello " will be returned. + */ + public static String getWordAtCursor( + InputConnection connection, String separators, Range range) { + Range r = getWordRangeAtCursor(connection, separators, range); + return (r == null) ? null : r.word; } /** @@ -87,7 +99,7 @@ public class EditingUtil { public static void deleteWordAtCursor( InputConnection connection, String separators) { - Range range = getWordRangeAtCursor(connection, separators); + Range range = getWordRangeAtCursor(connection, separators, null); if (range == null) return; connection.finishComposingText(); @@ -101,18 +113,20 @@ public class EditingUtil { /** * Represents a range of text, relative to the current cursor position. */ - private static class Range { + public static class Range { /** Characters before selection start */ - int charsBefore; + public int charsBefore; /** * Characters after selection start, including one trailing word * separator. */ - int charsAfter; + public int charsAfter; /** The actual characters that make up a word */ - String word; + public String word; + + public Range() {} public Range(int charsBefore, int charsAfter, String word) { if (charsBefore < 0 || charsAfter < 0) { @@ -125,7 +139,7 @@ public class EditingUtil { } private static Range getWordRangeAtCursor( - InputConnection connection, String sep) { + InputConnection connection, String sep, Range range) { if (connection == null || sep == null) { return null; } @@ -137,20 +151,22 @@ public class EditingUtil { // Find first word separator before the cursor int start = before.length(); - while (--start > 0 && !isWhitespace(before.charAt(start - 1), sep)); + while (start > 0 && !isWhitespace(before.charAt(start - 1), sep)) start--; // Find last word separator after the cursor int end = -1; while (++end < after.length() && !isWhitespace(after.charAt(end), sep)); - if (end < after.length() - 1) { - end++; // Include trailing space, if it exists, in word - } int cursor = getCursorPosition(connection); if (start >= 0 && cursor + end <= after.length() + before.length()) { String word = before.toString().substring(start, before.length()) - + after.toString().substring(0, end); - return new Range(before.length() - start, end, word); + + after.toString().substring(0, end); + + Range returnRange = range != null? range : new Range(); + returnRange.charsBefore = before.length() - start; + returnRange.charsAfter = end; + returnRange.word = word; + return returnRange; } return null; diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index b4ed80c1f..a89265e76 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -16,7 +16,6 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.voice.EditingUtil; import com.android.inputmethod.voice.FieldContext; import com.android.inputmethod.voice.SettingsUtil; import com.android.inputmethod.voice.VoiceInput; @@ -40,9 +39,9 @@ import android.os.Message; import android.os.SystemClock; import android.preference.PreferenceManager; import android.speech.SpeechRecognizer; -import android.text.AutoText; import android.text.ClipboardManager; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; @@ -78,6 +77,7 @@ public class LatinIME extends InputMethodService VoiceInput.UiListener, SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = "LatinIME"; + private static final boolean PERF_DEBUG = false; static final boolean DEBUG = false; static final boolean TRACE = false; static final boolean VOICE_INSTALLED = true; @@ -127,6 +127,7 @@ public class LatinIME extends InputMethodService private static final int MSG_UPDATE_SHIFT_STATE = 2; private static final int MSG_VOICE_RESULTS = 3; private static final int MSG_START_LISTENING_AFTER_SWIPE = 4; + private static final int MSG_UPDATE_OLD_SUGGESTIONS = 5; // If we detect a swipe gesture within N ms of typing, then swipe is // ignored, since it may in fact be two key presses in quick succession. @@ -189,7 +190,6 @@ public class LatinIME extends InputMethodService private boolean mAutoCorrectOn; private boolean mCapsLock; private boolean mPasswordText; - private boolean mEmailText; private boolean mVibrateOn; private boolean mSoundOn; private boolean mAutoCap; @@ -205,6 +205,12 @@ public class LatinIME extends InputMethodService private boolean mVoiceOnPrimary; private int mOrientation; private List<CharSequence> mSuggestPuncList; + // Keep track of the last selection range to decide if we need to show word alternatives + private int mLastSelectionStart; + private int mLastSelectionEnd; + + // Input type is such that we should not auto-correct + private boolean mInputTypeNoAutoCorrect; // Indicates whether the suggestion strip is to be on in landscape private boolean mJustAccepted; @@ -228,17 +234,66 @@ public class LatinIME extends InputMethodService // Keeps track of most recently inserted text (multi-character key) for reverting private CharSequence mEnteredText; + private boolean mRefreshKeyboardRequired; // For each word, a list of potential replacements, usually from voice. private Map<String, List<CharSequence>> mWordToSuggestions = new HashMap<String, List<CharSequence>>(); + private ArrayList<WordAlternatives> mWordHistory = new ArrayList<WordAlternatives>(); + private class VoiceResults { List<String> candidates; Map<String, List<CharSequence>> alternatives; } + + public abstract static class WordAlternatives { + protected CharSequence mChosenWord; - private boolean mRefreshKeyboardRequired; + public WordAlternatives() { + // Nothing + } + + public WordAlternatives(CharSequence chosenWord) { + mChosenWord = chosenWord; + } + + @Override + public int hashCode() { + return mChosenWord.hashCode(); + } + + public abstract CharSequence getOriginalWord(); + + public CharSequence getChosenWord() { + return mChosenWord; + } + + public abstract List<CharSequence> getAlternatives(); + } + + public class TypedWordAlternatives extends WordAlternatives { + private WordComposer word; + + public TypedWordAlternatives() { + // Nothing + } + + public TypedWordAlternatives(CharSequence chosenWord, WordComposer wordComposer) { + super(chosenWord); + word = wordComposer; + } + + @Override + public CharSequence getOriginalWord() { + return word.getTypedWord(); + } + + @Override + public List<CharSequence> getAlternatives() { + return getTypedSuggestions(word); + } + } Handler mHandler = new Handler() { @Override @@ -247,6 +302,9 @@ public class LatinIME extends InputMethodService case MSG_UPDATE_SUGGESTIONS: updateSuggestions(); break; + case MSG_UPDATE_OLD_SUGGESTIONS: + setOldSuggestions(); + break; case MSG_START_TUTORIAL: if (mTutorial == null) { if (mInputView.isShown()) { @@ -456,7 +514,6 @@ public class LatinIME extends InputMethodService mCompletionOn = false; mCompletions = null; mCapsLock = false; - mEmailText = false; mEnteredText = null; switch (attribute.inputType & EditorInfo.TYPE_MASK_CLASS) { @@ -479,9 +536,6 @@ public class LatinIME extends InputMethodService variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ) { mPredictionOn = false; } - if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) { - mEmailText = true; - } if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS || variation == EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME) { mAutoSpace = false; @@ -610,10 +664,11 @@ public class LatinIME extends InputMethodService // clear whatever candidate text we have. if ((((mComposing.length() > 0 && mPredicting) || mVoiceInputHighlighted) && (newSelStart != candidatesEnd - || newSelEnd != candidatesEnd))) { + || newSelEnd != candidatesEnd) + && mLastSelectionStart != newSelStart)) { mComposing.setLength(0); mPredicting = false; - updateSuggestions(); + postUpdateSuggestions(); TextEntryState.reset(); InputConnection ic = getCurrentInputConnection(); if (ic != null) { @@ -633,6 +688,21 @@ public class LatinIME extends InputMethodService mJustAccepted = false; postUpdateShiftKeyState(); + // Make a note of the cursor position + mLastSelectionStart = newSelStart; + mLastSelectionEnd = newSelEnd; + + + // If a word is selected + if ((candidatesStart == candidatesEnd || newSelStart != oldSelStart) + && (newSelStart < newSelEnd - 1 || (!mPredicting)) + && !mVoiceInputHighlighted) { + abortCorrection(false); + if (isCursorTouchingWord() || mLastSelectionStart < mLastSelectionEnd) { + postUpdateOldSuggestions(); + } + } + if (VOICE_INSTALLED) { if (mShowingVoiceSuggestions) { if (mImmediatelyAfterVoiceSuggestions) { @@ -675,13 +745,14 @@ public class LatinIME extends InputMethodService mVoiceInput.cancel(); } } + mWordHistory.clear(); super.hideWindow(); TextEntryState.endSession(); } @Override public void onDisplayCompletions(CompletionInfo[] completions) { - if (false) { + if (DEBUG) { Log.i("foo", "Received completions:"); for (int i=0; i<(completions != null ? completions.length : 0); i++) { Log.i("foo", " #" + i + ": " + completions[i]); @@ -699,7 +770,6 @@ public class LatinIME extends InputMethodService CompletionInfo ci = completions[i]; if (ci != null) stringList.add(ci.getText()); } - //CharSequence typedWord = mWord.getTypedWord(); setSuggestions(stringList, true, true, true); mBestWord = null; setCandidatesViewShown(isCandidateStripVisible() || mCompletionOn); @@ -724,6 +794,19 @@ public class LatinIME extends InputMethodService } @Override + public boolean onEvaluateFullscreenMode() { + DisplayMetrics dm = getResources().getDisplayMetrics(); + float displayHeight = dm.heightPixels; + // If the display is more than X inches high, don't go to fullscreen mode + float dimen = getResources().getDimension(R.dimen.max_height_for_fullscreen); + if (displayHeight > dimen) { + return false; + } else { + return super.onEvaluateFullscreenMode(); + } + } + + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: @@ -998,6 +1081,7 @@ public class LatinIME extends InputMethodService } InputConnection ic = getCurrentInputConnection(); if (ic == null) return; + abortCorrection(false); ic.beginBatchEdit(); if (mPredicting) { commitTyped(ic); @@ -1077,6 +1161,13 @@ public class LatinIME extends InputMethodService } } + private void abortCorrection(boolean force) { + if (force || TextEntryState.isCorrecting()) { + getCurrentInputConnection().finishComposingText(); + setSuggestions(null, false, false, false); + } + } + private void handleCharacter(int primaryCode, int[] keyCodes) { if (VOICE_INSTALLED && mVoiceInputHighlighted) { commitVoiceInput(); @@ -1086,11 +1177,13 @@ public class LatinIME extends InputMethodService // Assume input length is 1. This assumption fails for smiley face insertions. mVoiceInput.incrementTextModificationInsertCount(1); } + abortCorrection(false); if (isAlphabet(primaryCode) && isPredictionOn() && !isCursorTouchingWord()) { if (!mPredicting) { mPredicting = true; mComposing.setLength(0); + saveWordInHistory(mBestWord); mWord.reset(); } } @@ -1122,7 +1215,7 @@ public class LatinIME extends InputMethodService sendKeyChar((char)primaryCode); } updateShiftKeyState(getCurrentInputEditorInfo()); - measureCps(); + if (LatinIME.PERF_DEBUG) measureCps(); TextEntryState.typedCharacter((char) primaryCode, isWordSeparator(primaryCode)); } @@ -1141,6 +1234,7 @@ public class LatinIME extends InputMethodService InputConnection ic = getCurrentInputConnection(); if (ic != null) { ic.beginBatchEdit(); + abortCorrection(false); } if (mPredicting) { // In certain languages where single quote is a separator, it's better @@ -1180,7 +1274,6 @@ public class LatinIME extends InputMethodService && primaryCode != KEYCODE_ENTER) { swapPunctuationAndSpace(); } else if (isPredictionOn() && primaryCode == KEYCODE_SPACE) { - //else if (TextEntryState.STATE_SPACE_AFTER_ACCEPTED) { doubleSpace(); } if (pickedDefault && mBestWord != null) { @@ -1202,6 +1295,17 @@ public class LatinIME extends InputMethodService TextEntryState.endSession(); } + private void saveWordInHistory(CharSequence result) { + if (mWord.size() <= 1) { + mWord.reset(); + return; + } + TypedWordAlternatives entry = new TypedWordAlternatives(result, mWord); + // Create a new WordComposer as the old one is being saved for later use + mWord = new WordComposer(mWord); + mWordHistory.add(entry); + } + private void checkToggleCapsLock() { if (mInputView.getKeyboard().isShifted()) { toggleCapsLock(); @@ -1220,6 +1324,11 @@ public class LatinIME extends InputMethodService mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_SUGGESTIONS), 100); } + private void postUpdateOldSuggestions() { + mHandler.removeMessages(MSG_UPDATE_OLD_SUGGESTIONS); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_OLD_SUGGESTIONS), 300); + } + private boolean isPredictionOn() { boolean predictionOn = mPredictionOn; return predictionOn; @@ -1444,24 +1553,45 @@ public class LatinIME extends InputMethodService setNextSuggestions(); return; } + showSuggestions(mWord); + } + + private List<CharSequence> getTypedSuggestions(WordComposer word) { + List<CharSequence> stringList = mSuggest.getSuggestions(mInputView, word, false); + return stringList; + } - List<CharSequence> stringList = mSuggest.getSuggestions(mInputView, mWord, false); + private void showCorrections(WordAlternatives alternatives) { + List<CharSequence> stringList = alternatives.getAlternatives(); + ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(null); + showSuggestions(stringList, alternatives.getOriginalWord(), false, false); + } + + private void showSuggestions(WordComposer word) { + List<CharSequence> stringList = mSuggest.getSuggestions(mInputView, word, false); int[] nextLettersFrequencies = mSuggest.getNextLettersFrequencies(); ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(nextLettersFrequencies); boolean correctionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasMinimalCorrection(); //|| mCorrectionMode == mSuggest.CORRECTION_FULL; - CharSequence typedWord = mWord.getTypedWord(); + CharSequence typedWord = word.getTypedWord(); // If we're in basic correct boolean typedWordValid = mSuggest.isValidWord(typedWord) || - (preferCapitalization() && mSuggest.isValidWord(typedWord.toString().toLowerCase())); + (preferCapitalization() + && mSuggest.isValidWord(typedWord.toString().toLowerCase())); if (mCorrectionMode == Suggest.CORRECTION_FULL) { correctionAvailable |= typedWordValid; } // Don't auto-correct words with multiple capital letter - correctionAvailable &= !mWord.isMostlyCaps(); + correctionAvailable &= !word.isMostlyCaps(); + correctionAvailable &= !TextEntryState.isCorrecting(); + showSuggestions(stringList, typedWord, typedWordValid, correctionAvailable); + } + + private void showSuggestions(List<CharSequence> stringList, CharSequence typedWord, + boolean typedWordValid, boolean correctionAvailable) { setSuggestions(stringList, false, typedWordValid, correctionAvailable); if (stringList.size() > 0) { if (correctionAvailable && !typedWordValid && stringList.size() > 1) { @@ -1499,6 +1629,7 @@ public class LatinIME extends InputMethodService mVoiceInput.logTextModifiedByChooseSuggestion(suggestion.length()); } + final boolean correcting = TextEntryState.isCorrecting(); InputConnection ic = getCurrentInputConnection(); if (ic != null) { ic.beginBatchEdit(); @@ -1536,10 +1667,11 @@ public class LatinIME extends InputMethodService } TextEntryState.acceptedSuggestion(mComposing.toString(), suggestion); // Follow it with a space - if (mAutoSpace) { + if (mAutoSpace && !correcting) { sendSpace(); mJustAddedAutoSpace = true; } + // Fool the state watcher so that a subsequent backspace will not do a revert TextEntryState.typedCharacter((char) KEYCODE_SPACE, true); if (index == 0 && mCorrectionMode > 0 && !mSuggest.isValidWord(suggestion)) { @@ -1567,6 +1699,7 @@ public class LatinIME extends InputMethodService ic.commitText(suggestion, 1); } } + saveWordInHistory(suggestion); mPredicting = false; mCommittedLength = suggestion.length(); ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(null); @@ -1574,16 +1707,99 @@ public class LatinIME extends InputMethodService updateShiftKeyState(getCurrentInputEditorInfo()); } + private void setOldSuggestions() { + // TODO: Inefficient to check if touching word and then get the touching word. Do it + // in one go. + InputConnection ic = getCurrentInputConnection(); + if (ic == null) return; + ic.beginBatchEdit(); + // If there is a selection, then undo the selection first. Unfortunately this causes + // a flicker. TODO: Add getSelectionText() to InputConnection API. + if (mLastSelectionStart < mLastSelectionEnd) { + ic.setSelection(mLastSelectionStart, mLastSelectionStart); + } + if (!mPredicting && isCursorTouchingWord()) { + EditingUtil.Range range = new EditingUtil.Range(); + CharSequence touching = + EditingUtil.getWordAtCursor(getCurrentInputConnection(), mWordSeparators, + range); + if (touching != null && touching.length() > 1) { + if (mWordSeparators.indexOf(touching.charAt(touching.length() - 1)) > 0) { + touching = touching.toString().substring(0, touching.length() - 1); + } + // Search for result in word history + WordComposer foundWord = null; + WordAlternatives alternatives = null; + for (WordAlternatives entry : mWordHistory) { + if (TextUtils.equals(entry.getChosenWord(), touching)) { + if (entry instanceof TypedWordAlternatives) { + foundWord = ((TypedWordAlternatives)entry).word; + } + alternatives = entry; + break; + } + } + // If we didn't find a match, at least suggest completions + if (foundWord == null && mSuggest.isValidWord(touching)) { + foundWord = new WordComposer(); + for (int i = 0; i < touching.length(); i++) { + foundWord.add(touching.charAt(i), new int[] { touching.charAt(i) }); + } + } + // Found a match, show suggestions + if (foundWord != null || alternatives != null) { + mSuggestionShouldReplaceCurrentWord = true; + underlineWord(touching, range.charsBefore, range.charsAfter); + TextEntryState.selectedForCorrection(); + if (alternatives == null) alternatives = new TypedWordAlternatives(touching, + foundWord); + showCorrections(alternatives); + if (foundWord != null) { + mWord = foundWord; + } else { + mWord.reset(); + } + // Revert the selection + if (mLastSelectionStart < mLastSelectionEnd) { + ic.setSelection(mLastSelectionStart, mLastSelectionEnd); + } + ic.endBatchEdit(); + return; + } + abortCorrection(true); + } else { + abortCorrection(true); + setNextSuggestions(); + } + } else { + abortCorrection(true); + } + // Revert the selection + if (mLastSelectionStart < mLastSelectionEnd) { + ic.setSelection(mLastSelectionStart, mLastSelectionEnd); + } + ic.endBatchEdit(); + } + private void setNextSuggestions() { setSuggestions(mSuggestPuncList, false, false, false); } + private void underlineWord(CharSequence word, int left, int right) { + InputConnection ic = getCurrentInputConnection(); + if (ic == null) return; + ic.deleteSurroundingText(left, right); + ic.setComposingText(word, 1); + ic.setSelection(mLastSelectionStart, mLastSelectionStart); + } + private void checkAddToDictionary(CharSequence suggestion, int frequencyDelta) { + if (suggestion == null || suggestion.length() < 1) return; // Only auto-add to dictionary if auto-correct is ON. Otherwise we'll be // adding words in situations where the user or application really didn't // want corrections enabled or learned. if (!(mCorrectionMode == Suggest.CORRECTION_FULL)) return; - if (mAutoDictionary.isValidWord(suggestion) + if (suggestion != null && mAutoDictionary.isValidWord(suggestion) || (!mSuggest.isValidWord(suggestion.toString()) && !mSuggest.isValidWord(suggestion.toString().toLowerCase()))) { mAutoDictionary.addWord(suggestion.toString(), frequencyDelta); @@ -1858,7 +2074,7 @@ public class LatinIME extends InputMethodService launchSettings(LatinIMESettings.class); } - protected void launchSettings(Class settingsClass) { + protected void launchSettings(Class<LatinIMESettings> settingsClass) { handleClose(); Intent intent = new Intent(); intent.setClass(LatinIME.this, settingsClass); @@ -1995,15 +2211,12 @@ public class LatinIME extends InputMethodService // Characters per second measurement - private static final boolean PERF_DEBUG = false; private long mLastCpsTime; private static final int CPS_BUFFER_SIZE = 16; private long[] mCpsIntervals = new long[CPS_BUFFER_SIZE]; private int mCpsIndex; - private boolean mInputTypeNoAutoCorrect; private void measureCps() { - if (!LatinIME.PERF_DEBUG) return; long now = System.currentTimeMillis(); if (mLastCpsTime == 0) mLastCpsTime = now - 100; // Initial mCpsIntervals[mCpsIndex] = now - mLastCpsTime; diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboard.java b/java/src/com/android/inputmethod/latin/LatinKeyboard.java index 6aea5d13a..ea6b74e1b 100644 --- a/java/src/com/android/inputmethod/latin/LatinKeyboard.java +++ b/java/src/com/android/inputmethod/latin/LatinKeyboard.java @@ -47,7 +47,6 @@ public class LatinKeyboard extends Keyboard { private Drawable mShiftLockIcon; private Drawable mShiftLockPreviewIcon; private Drawable mOldShiftIcon; - private Drawable mOldShiftPreviewIcon; private Drawable mSpaceIcon; private Drawable mSpacePreviewIcon; private Drawable mMicIcon; @@ -68,7 +67,6 @@ public class LatinKeyboard extends Keyboard { private LanguageSwitcher mLanguageSwitcher; private Resources mRes; private Context mContext; - private int mMode; // Whether this keyboard has voice icon on it private boolean mHasVoiceButton; // Whether voice icon is enabled at all @@ -77,9 +75,7 @@ public class LatinKeyboard extends Keyboard { private CharSequence m123Label; private boolean mCurrentlyInSpace; private SlidingLocaleDrawable mSlidingLocaleIcon; - private Rect mBounds = new Rect(); private int[] mPrefLetterFrequencies; - private boolean mPreemptiveCorrection; private int mPrefLetter; private int mPrefLetterX; private int mPrefLetterY; @@ -107,7 +103,6 @@ public class LatinKeyboard extends Keyboard { super(context, xmlLayoutResId, mode); final Resources res = context.getResources(); mContext = context; - mMode = mode; mRes = res; mShiftLockIcon = res.getDrawable(R.drawable.sym_keyboard_shift_locked); mShiftLockPreviewIcon = res.getDrawable(R.drawable.sym_keyboard_feedback_shift_locked); @@ -224,7 +219,6 @@ public class LatinKeyboard extends Keyboard { ((LatinKey)mShiftKey).enableShiftLock(); } mOldShiftIcon = mShiftKey.icon; - mOldShiftPreviewIcon = mShiftKey.iconPreview; } } @@ -338,21 +332,19 @@ public class LatinKeyboard extends Keyboard { } private void drawSpaceBar(Canvas canvas, int width, int height, int opacity) { - canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); + canvas.drawColor(mRes.getColor(R.color.latinkeyboard_transparent), PorterDuff.Mode.CLEAR); Paint paint = new Paint(); paint.setAntiAlias(true); paint.setAlpha(opacity); // Get the text size from the theme paint.setTextSize(getTextSizeFromTheme(android.R.style.TextAppearance_Small, 14)); paint.setTextAlign(Align.CENTER); - //// Draw a drop shadow for the text - //paint.setShadowLayer(2f, 0, 0, 0xFF000000); final String language = getInputLanguage(mSpaceKey.width, paint); final int ascent = (int) -paint.ascent(); - paint.setColor(0x80000000); + paint.setColor(mRes.getColor(R.color.latinkeyboard_bar_language_shadow)); canvas.drawText(language, width / 2, ascent - 1, paint); - paint.setColor(0xFF808080); + paint.setColor(mRes.getColor(R.color.latinkeyboard_bar_language_text)); canvas.drawText(language, width / 2, ascent, paint); // Put arrows on either side of the text @@ -503,9 +495,10 @@ public class LatinKeyboard extends Keyboard { // Handle preferred next letter final int[] pref = mPrefLetterFrequencies; if (mPrefLetter > 0) { - if (DEBUG_PREFERRED_LETTER && mPrefLetter == code - && !key.isInsideSuper(x, y)) { - Log.d(TAG, "CORRECTED !!!!!!"); + if (DEBUG_PREFERRED_LETTER) { + if (mPrefLetter == code && !key.isInsideSuper(x, y)) { + Log.d(TAG, "CORRECTED !!!!!!"); + } } return mPrefLetter == code; } else { @@ -718,7 +711,7 @@ public class LatinKeyboard extends Keyboard { public void draw(Canvas canvas) { canvas.save(); if (mHitThreshold) { - mTextPaint.setColor(0xFF000000); + mTextPaint.setColor(mRes.getColor(R.color.latinkeyboard_text_color)); canvas.clipRect(0, 0, mWidth, mHeight); if (mCurrentLanguage == null) { mCurrentLanguage = getInputLanguage(mWidth, mTextPaint); diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardView.java index 74fc475e6..323f4bf6b 100644 --- a/java/src/com/android/inputmethod/latin/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/latin/LatinKeyboardView.java @@ -23,7 +23,6 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.inputmethodservice.Keyboard; import android.inputmethodservice.KeyboardView; -import android.inputmethodservice.KeyboardView.OnKeyboardActionListener; import android.inputmethodservice.Keyboard.Key; import android.os.Handler; import android.os.Message; @@ -452,27 +451,30 @@ public class LatinKeyboardView extends KeyboardView { } } } - - void startPlaying(String s) { - if (!DEBUG_AUTO_PLAY) return; - if (s == null) return; - mStringToPlay = s.toLowerCase(); - mPlaying = true; - mDownDelivered = false; - mStringIndex = 0; - mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 10); + + public void startPlaying(String s) { + if (DEBUG_AUTO_PLAY) { + if (s == null) return; + mStringToPlay = s.toLowerCase(); + mPlaying = true; + mDownDelivered = false; + mStringIndex = 0; + mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 10); + } } @Override public void draw(Canvas c) { super.draw(c); - if (DEBUG_AUTO_PLAY && mPlaying) { - mHandler2.removeMessages(MSG_TOUCH_DOWN); - mHandler2.removeMessages(MSG_TOUCH_UP); - if (mDownDelivered) { - mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_UP, 20); - } else { - mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 20); + if (DEBUG_AUTO_PLAY) { + if (mPlaying) { + mHandler2.removeMessages(MSG_TOUCH_DOWN); + mHandler2.removeMessages(MSG_TOUCH_UP); + if (mDownDelivered) { + mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_UP, 20); + } else { + mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 20); + } } } if (DEBUG_LINE) { diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index a70bea003..010913d6d 100755 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -16,18 +16,17 @@ package com.android.inputmethod.latin; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import android.content.Context; import android.text.AutoText; import android.text.TextUtils; import android.util.Log; import android.view.View; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import com.android.inputmethod.latin.WordComposer; - /** * This class loads a dictionary and provides a list of suggestions for a given sequence of * characters. This includes corrections and completions. @@ -69,9 +68,17 @@ public class Suggest implements Dictionary.WordCallback { private int mCorrectionMode = CORRECTION_BASIC; - public Suggest(Context context, int dictionaryResId) { mMainDict = new BinaryDictionary(context, dictionaryResId); + initPool(); + } + + public Suggest(Context context, ByteBuffer byteBuffer) { + mMainDict = new BinaryDictionary(context, byteBuffer); + initPool(); + } + + private void initPool() { for (int i = 0; i < mPrefMaxSuggestions; i++) { StringBuilder sb = new StringBuilder(32); mStringPool.add(sb); diff --git a/java/src/com/android/inputmethod/latin/TextEntryState.java b/java/src/com/android/inputmethod/latin/TextEntryState.java index d056ceb16..8fd9b7129 100644 --- a/java/src/com/android/inputmethod/latin/TextEntryState.java +++ b/java/src/com/android/inputmethod/latin/TextEntryState.java @@ -69,9 +69,11 @@ public class TextEntryState { public static final int STATE_SPACE_AFTER_ACCEPTED = 7; public static final int STATE_SPACE_AFTER_PICKED = 8; public static final int STATE_UNDO_COMMIT = 9; - + public static final int STATE_CORRECTING = 10; + public static final int STATE_PICKED_CORRECTION = 11; + private static int sState = STATE_UNKNOWN; - + private static FileOutputStream sKeyLocationFile; private static FileOutputStream sUserActionFile; @@ -139,12 +141,17 @@ public class TextEntryState { public static void acceptedSuggestion(CharSequence typedWord, CharSequence actualWord) { sManualSuggestCount++; + int oldState = sState; if (typedWord.equals(actualWord)) { acceptedTyped(typedWord); } - sState = STATE_PICKED_SUGGESTION; + sState = oldState == STATE_CORRECTING ? STATE_PICKED_CORRECTION : STATE_PICKED_SUGGESTION; } - + + public static void selectedForCorrection() { + sState = STATE_CORRECTING; + } + public static void typedCharacter(char c, boolean isSeparator) { boolean isSpace = c == ' '; switch (sState) { @@ -166,6 +173,7 @@ public class TextEntryState { } break; case STATE_PICKED_SUGGESTION: + case STATE_PICKED_CORRECTION: if (isSpace) { sState = STATE_SPACE_AFTER_PICKED; } else if (isSeparator) { @@ -192,6 +200,10 @@ public class TextEntryState { } else { sState = STATE_IN_WORD; } + break; + case STATE_CORRECTING: + sState = STATE_START; + break; } } @@ -212,7 +224,11 @@ public class TextEntryState { public static int getState() { return sState; } - + + public static boolean isCorrecting() { + return sState == STATE_CORRECTING || sState == STATE_PICKED_CORRECTION; + } + public static void keyPressedAt(Key key, int x, int y) { if (LOGGING && sKeyLocationFile != null && key.codes[0] >= 32) { String out = diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 19f714ae7..e2573a0a9 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -44,11 +44,18 @@ public class WordComposer { */ private boolean mIsCapitalized; - WordComposer() { + public WordComposer() { mCodes = new ArrayList<int[]>(12); mTypedWord = new StringBuilder(20); } + WordComposer(WordComposer copy) { + mCodes = (ArrayList<int[]>) copy.mCodes.clone(); + mPreferredWord = copy.mPreferredWord; + mTypedWord = new StringBuilder(copy.mTypedWord); + mCapsCount = copy.mCapsCount; + mAutoCapitalized = copy.mAutoCapitalized; + } /** * Clear out the keys registered so far. */ diff --git a/java/src/com/android/inputmethod/voice/LatinIMEWithVoice.java b/java/src/com/android/inputmethod/voice/LatinIMEWithVoice.java deleted file mode 100644 index ccbf5b6bc..000000000 --- a/java/src/com/android/inputmethod/voice/LatinIMEWithVoice.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * 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.android.inputmethod.voice; - -import android.content.Intent; - -import com.android.inputmethod.latin.LatinIME; - -public class LatinIMEWithVoice extends LatinIME { - @Override - protected void launchSettings() { - launchSettings(LatinIMEWithVoiceSettings.class); - } -} diff --git a/java/src/com/android/inputmethod/voice/LatinIMEWithVoiceSettings.java b/java/src/com/android/inputmethod/voice/LatinIMEWithVoiceSettings.java deleted file mode 100644 index 13a58e14d..000000000 --- a/java/src/com/android/inputmethod/voice/LatinIMEWithVoiceSettings.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * 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.android.inputmethod.voice; - -import com.android.inputmethod.latin.LatinIMESettings; - -public class LatinIMEWithVoiceSettings extends LatinIMESettings {} diff --git a/java/src/com/google/android/voicesearch/LatinIMEWithVoice.java b/java/src/com/google/android/voicesearch/LatinIMEWithVoice.java deleted file mode 100644 index 8a339d14a..000000000 --- a/java/src/com/google/android/voicesearch/LatinIMEWithVoice.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * - * Copyright (C) 2009 Google Inc. - * - * 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.google.android.voicesearch; - -import android.content.Intent; - -import com.android.inputmethod.latin.LatinIME; - -public class LatinIMEWithVoice extends LatinIME { - @Override - protected void launchSettings() { - launchSettings(LatinIMEWithVoiceSettings.class); - } -} diff --git a/java/src/com/google/android/voicesearch/LatinIMEWithVoiceSettings.java b/java/src/com/google/android/voicesearch/LatinIMEWithVoiceSettings.java deleted file mode 100644 index a53cebfd9..000000000 --- a/java/src/com/google/android/voicesearch/LatinIMEWithVoiceSettings.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.google.android.voicesearch; - -import com.android.inputmethod.latin.LatinIMESettings; - -public class LatinIMEWithVoiceSettings extends LatinIMESettings {} |