From 90599bd1074b9b82e89f9bb080cf45b49df2876c Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Fri, 21 May 2010 17:03:22 -0700 Subject: [PATCH 1/6] Fix bug: 2693836 - Russian keyboard is missing a letter --- java/res/xml-ru/kbd_qwerty.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java/res/xml-ru/kbd_qwerty.xml b/java/res/xml-ru/kbd_qwerty.xml index cbb518f24..9773a305f 100755 --- a/java/res/xml-ru/kbd_qwerty.xml +++ b/java/res/xml-ru/kbd_qwerty.xml @@ -70,7 +70,9 @@ - + Date: Wed, 9 Jun 2010 22:51:48 -0700 Subject: [PATCH 2/6] Import revised translations Change-Id: I7ffd12b24b3946d83899813c498c32eea0b75de0 --- java/res/values-ko/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index 350957939..c4bc6cf13 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -87,7 +87,7 @@ "음성 입력은 Google의 네트워크화된 음성 인식을 사용하는 실험적 기능입니다." "음성 입력을 사용하지 않으려면 키보드 설정으로 이동하세요." "음성 입력을 사용하려면 마이크 버튼을 누르거나 터치 키보드 위로 손가락을 미끄러지듯 움직이세요." - "지금 시작하세요." + "지금 말하세요." "인식 중" From 7697133c85f30ae2ec046d9fd19fdb5f2ddc7cbc Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Mon, 21 Jun 2010 11:05:32 -0700 Subject: [PATCH 3/6] Import revised translations Change-Id: If4c56c6cea6a99cc784e3e16457a43ca550fdefa --- java/res/values-ko/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index c4bc6cf13..94d1ff4ef 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -23,7 +23,7 @@ "Android 키보드" "Android 키보드 설정" "키를 누를 때 진동 발생" - "버튼을 누를 때 소리 발생" + "키를 누를 때 소리 발생" "입력 오류 수정" "입력 오류 수정 사용" "가로 입력 오류" From a62f7c18f9cb8feb179bd865fc9caa65cfc80b4a Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Sat, 27 Feb 2010 07:53:56 -0800 Subject: [PATCH 4/6] Remember typed words for auto-corrections Change-Id: I475c2fa7604f91659100510079fa13c7615177e9 --- .../{voice => latin}/EditingUtil.java | 48 ++-- .../android/inputmethod/latin/LatinIME.java | 232 +++++++++++++++++- .../inputmethod/latin/TextEntryState.java | 26 +- .../inputmethod/latin/WordComposer.java | 7 + 4 files changed, 279 insertions(+), 34 deletions(-) rename java/src/com/android/inputmethod/{voice => latin}/EditingUtil.java (78%) diff --git a/java/src/com/android/inputmethod/voice/EditingUtil.java b/java/src/com/android/inputmethod/latin/EditingUtil.java similarity index 78% rename from java/src/com/android/inputmethod/voice/EditingUtil.java rename to 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 db0a822ed..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; @@ -128,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. @@ -205,6 +205,12 @@ public class LatinIME extends InputMethodService private boolean mVoiceOnPrimary; private int mOrientation; private List 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> mWordToSuggestions = new HashMap>(); + private ArrayList mWordHistory = new ArrayList(); + private class VoiceResults { List candidates; Map> 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 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 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()) { @@ -606,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) { @@ -629,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) { @@ -671,6 +745,7 @@ public class LatinIME extends InputMethodService mVoiceInput.cancel(); } } + mWordHistory.clear(); super.hideWindow(); TextEntryState.endSession(); } @@ -695,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); @@ -1007,6 +1081,7 @@ public class LatinIME extends InputMethodService } InputConnection ic = getCurrentInputConnection(); if (ic == null) return; + abortCorrection(false); ic.beginBatchEdit(); if (mPredicting) { commitTyped(ic); @@ -1086,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(); @@ -1095,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(); } } @@ -1150,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 @@ -1189,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) { @@ -1211,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(); @@ -1229,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; @@ -1453,24 +1553,45 @@ public class LatinIME extends InputMethodService setNextSuggestions(); return; } + showSuggestions(mWord); + } - List stringList = mSuggest.getSuggestions(mInputView, mWord, false); + private List getTypedSuggestions(WordComposer word) { + List stringList = mSuggest.getSuggestions(mInputView, word, false); + return stringList; + } + + private void showCorrections(WordAlternatives alternatives) { + List stringList = alternatives.getAlternatives(); + ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(null); + showSuggestions(stringList, alternatives.getOriginalWord(), false, false); + } + + private void showSuggestions(WordComposer word) { + List 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 stringList, CharSequence typedWord, + boolean typedWordValid, boolean correctionAvailable) { setSuggestions(stringList, false, typedWordValid, correctionAvailable); if (stringList.size() > 0) { if (correctionAvailable && !typedWordValid && stringList.size() > 1) { @@ -1508,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(); @@ -1545,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)) { @@ -1576,6 +1699,7 @@ public class LatinIME extends InputMethodService ic.commitText(suggestion, 1); } } + saveWordInHistory(suggestion); mPredicting = false; mCommittedLength = suggestion.length(); ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(null); @@ -1583,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); @@ -2008,7 +2215,6 @@ public class LatinIME extends InputMethodService 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() { long now = System.currentTimeMillis(); 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 2547aa133..e2573a0a9 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -49,6 +49,13 @@ public class WordComposer { mTypedWord = new StringBuilder(20); } + WordComposer(WordComposer copy) { + mCodes = (ArrayList) copy.mCodes.clone(); + mPreferredWord = copy.mPreferredWord; + mTypedWord = new StringBuilder(copy.mTypedWord); + mCapsCount = copy.mCapsCount; + mAutoCapitalized = copy.mAutoCapitalized; + } /** * Clear out the keys registered so far. */ From 578096b6881270ccb4f28e30277dd148cda5dbef Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Fri, 25 Jun 2010 14:21:02 -0700 Subject: [PATCH 5/6] Quick adjustment to keyboard size on xlarge screens. And adjust the text size of the extension keyboard too. --- java/res/layout-xlarge/input.xml | 31 ++++++++++++++++++++++++++ java/res/layout-xlarge/input_trans.xml | 31 ++++++++++++++++++++++++++ java/res/values-xlarge/dimens.xml | 25 +++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 java/res/layout-xlarge/input.xml create mode 100755 java/res/layout-xlarge/input_trans.xml create mode 100644 java/res/values-xlarge/dimens.xml diff --git a/java/res/layout-xlarge/input.xml b/java/res/layout-xlarge/input.xml new file mode 100644 index 000000000..6b0bb12a6 --- /dev/null +++ b/java/res/layout-xlarge/input.xml @@ -0,0 +1,31 @@ + + + + diff --git a/java/res/layout-xlarge/input_trans.xml b/java/res/layout-xlarge/input_trans.xml new file mode 100755 index 000000000..1dfdc1555 --- /dev/null +++ b/java/res/layout-xlarge/input_trans.xml @@ -0,0 +1,31 @@ + + + + diff --git a/java/res/values-xlarge/dimens.xml b/java/res/values-xlarge/dimens.xml new file mode 100644 index 000000000..433176274 --- /dev/null +++ b/java/res/values-xlarge/dimens.xml @@ -0,0 +1,25 @@ + + + + + 72dip + 46dip + 0dip + From 57691a35743e3705abe60db4902f9ad3c0e722d8 Mon Sep 17 00:00:00 2001 From: Maryam Garrett Date: Mon, 28 Jun 2010 16:31:12 -0400 Subject: [PATCH 6/6] Show word alternatives for spoken input With this change, we show alternatives for a word that has been spoken.The user triggers the display of alternatives by tapping on the word. The alternatives are currently generated by a client-side algorithm, but will be improved when we move to a server-side generation of the alternatives Change-Id: I454ee28fef9bf97a1b026ce8c605ca4badb39e79 --- .../android/inputmethod/latin/LatinIME.java | 83 ++++++++++++------- .../android/inputmethod/voice/VoiceInput.java | 4 +- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index a89265e76..c19428d05 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -508,7 +508,6 @@ public class LatinIME extends InputMethodService mShowingVoiceSuggestions = false; mImmediatelyAfterVoiceSuggestions = false; mVoiceInputHighlighted = false; - mWordToSuggestions.clear(); mInputTypeNoAutoCorrect = false; mPredictionOn = false; mCompletionOn = false; @@ -702,29 +701,6 @@ public class LatinIME extends InputMethodService postUpdateOldSuggestions(); } } - - if (VOICE_INSTALLED) { - if (mShowingVoiceSuggestions) { - if (mImmediatelyAfterVoiceSuggestions) { - mImmediatelyAfterVoiceSuggestions = false; - } else { - updateSuggestions(); - mShowingVoiceSuggestions = false; - } - } - if (VoiceInput.ENABLE_WORD_CORRECTIONS) { - // If we have alternatives for the current word, then show them. - String word = EditingUtil.getWordAtCursor( - getCurrentInputConnection(), getWordSeparators()); - if (word != null && mWordToSuggestions.containsKey(word.trim())) { - mSuggestionShouldReplaceCurrentWord = true; - final List suggestions = mWordToSuggestions.get(word.trim()); - - setSuggestions(suggestions, false, true, true); - setCandidatesViewShown(true); - } - } - } } @Override @@ -745,6 +721,7 @@ public class LatinIME extends InputMethodService mVoiceInput.cancel(); } } + mWordToSuggestions.clear(); mWordHistory.clear(); super.hideWindow(); TextEntryState.endSession(); @@ -1512,9 +1489,6 @@ public class LatinIME extends InputMethodService // Show N-Best alternates, if there is more than one choice. if (nBest.size() > 1) { mImmediatelyAfterVoiceSuggestions = true; - mShowingVoiceSuggestions = true; - setSuggestions(nBest.subList(1, nBest.size()), false, true, true); - setCandidatesViewShown(true); } mVoiceInputHighlighted = true; mWordToSuggestions.putAll(mVoiceResults.alternatives); @@ -1682,6 +1656,29 @@ public class LatinIME extends InputMethodService } } + private void rememberReplacedWord(CharSequence suggestion) { + if (mShowingVoiceSuggestions) { + // Retain the replaced word in the alternatives array. + InputConnection ic = getCurrentInputConnection(); + EditingUtil.Range range = new EditingUtil.Range(); + String wordToBeReplaced = EditingUtil.getWordAtCursor(getCurrentInputConnection(), + mWordSeparators, range).trim(); + if (!mWordToSuggestions.containsKey(wordToBeReplaced)) { + wordToBeReplaced = wordToBeReplaced.toLowerCase(); + } + if (mWordToSuggestions.containsKey(wordToBeReplaced)) { + List suggestions = mWordToSuggestions.get(wordToBeReplaced); + if (suggestions.contains(suggestion)) { + suggestions.remove(suggestion); + } + suggestions.add(wordToBeReplaced); + mWordToSuggestions.remove(wordToBeReplaced); + mWordToSuggestions.put(suggestion.toString(), suggestions); + } + } + // TODO: implement rememberReplacedWord for typed words + } + private void pickSuggestion(CharSequence suggestion) { if (mCapsLock) { suggestion = suggestion.toString().toUpperCase(); @@ -1692,6 +1689,7 @@ public class LatinIME extends InputMethodService } InputConnection ic = getCurrentInputConnection(); if (ic != null) { + rememberReplacedWord(suggestion); if (mSuggestionShouldReplaceCurrentWord) { EditingUtil.deleteWordAtCursor(ic, getWordSeparators()); } @@ -1710,6 +1708,7 @@ public class LatinIME extends InputMethodService private void setOldSuggestions() { // TODO: Inefficient to check if touching word and then get the touching word. Do it // in one go. + mShowingVoiceSuggestions = false; InputConnection ic = getCurrentInputConnection(); if (ic == null) return; ic.beginBatchEdit(); @@ -1727,7 +1726,35 @@ public class LatinIME extends InputMethodService if (mWordSeparators.indexOf(touching.charAt(touching.length() - 1)) > 0) { touching = touching.toString().substring(0, touching.length() - 1); } - // Search for result in word history + + // Search for result in spoken word alternatives + // TODO: possibly combine the spoken suggestions with the typed suggestions. + String selectedWord = touching.toString().trim(); + if (!mWordToSuggestions.containsKey(selectedWord)){ + selectedWord = selectedWord.toLowerCase(); + } + if (mWordToSuggestions.containsKey(selectedWord)){ + mShowingVoiceSuggestions = true; + mSuggestionShouldReplaceCurrentWord = true; + underlineWord(touching, range.charsBefore, range.charsAfter); + List suggestions = mWordToSuggestions.get(selectedWord); + // If the first letter of touching is capitalized, make all the suggestions + // start with a capital letter. + if (Character.isUpperCase((char) touching.charAt(0))) { + for (int i=0; i< suggestions.size(); i++) { + String origSugg = (String) suggestions.get(i); + String capsSugg = origSugg.toUpperCase().charAt(0) + + origSugg.subSequence(1, origSugg.length()).toString(); + suggestions.set(i,capsSugg); + } + } + setSuggestions(suggestions, false, true, true); + setCandidatesViewShown(true); + TextEntryState.selectedForCorrection(); + ic.endBatchEdit(); + return; + } + // If we didn't find a match, search for result in word history WordComposer foundWord = null; WordAlternatives alternatives = null; for (WordAlternatives entry : mWordHistory) { diff --git a/java/src/com/android/inputmethod/voice/VoiceInput.java b/java/src/com/android/inputmethod/voice/VoiceInput.java index ac06ab50d..354670969 100644 --- a/java/src/com/android/inputmethod/voice/VoiceInput.java +++ b/java/src/com/android/inputmethod/voice/VoiceInput.java @@ -63,7 +63,7 @@ public class VoiceInput implements OnClickListener { // WARNING! Before enabling this, fix the problem with calling getExtractedText() in // landscape view. It causes Extracted text updates to be rejected due to a token mismatch - public static boolean ENABLE_WORD_CORRECTIONS = false; + public static boolean ENABLE_WORD_CORRECTIONS = true; // Dummy word suggestion which means "delete current word" public static final String DELETE_SYMBOL = " \u00D7 "; // times symbol @@ -308,7 +308,7 @@ public class VoiceInput implements OnClickListener { SettingsUtil.getSettingsInt( mContext.getContentResolver(), SettingsUtil.LATIN_IME_MAX_VOICE_RESULTS, - 1)); + 10)); // Get endpointer params from Gservices. // TODO: Consider caching these values for improved performance on slower devices.