diff options
Diffstat (limited to 'java/src/com/android/inputmethod/voice')
4 files changed, 57 insertions, 230 deletions
diff --git a/java/src/com/android/inputmethod/voice/EditingUtil.java b/java/src/com/android/inputmethod/voice/EditingUtil.java deleted file mode 100644 index 6316d8ccf..000000000 --- a/java/src/com/android/inputmethod/voice/EditingUtil.java +++ /dev/null @@ -1,162 +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.view.inputmethod.ExtractedText; -import android.view.inputmethod.ExtractedTextRequest; -import android.view.inputmethod.InputConnection; - -/** - * Utility methods to deal with editing text through an InputConnection. - */ -public class EditingUtil { - private EditingUtil() {}; - - /** - * Append newText to the text field represented by connection. - * The new text becomes selected. - */ - public static void appendText(InputConnection connection, String newText) { - if (connection == null) { - return; - } - - // Commit the composing text - connection.finishComposingText(); - - // Add a space if the field already has text. - CharSequence charBeforeCursor = connection.getTextBeforeCursor(1, 0); - if (charBeforeCursor != null - && !charBeforeCursor.equals(" ") - && (charBeforeCursor.length() > 0)) { - newText = " " + newText; - } - - connection.setComposingText(newText, 1); - } - - private static int getCursorPosition(InputConnection connection) { - ExtractedText extracted = connection.getExtractedText( - new ExtractedTextRequest(), 0); - if (extracted == null) { - return -1; - } - return extracted.startOffset + extracted.selectionStart; - } - - private static int getSelectionEnd(InputConnection connection) { - ExtractedText extracted = connection.getExtractedText( - new ExtractedTextRequest(), 0); - if (extracted == null) { - return -1; - } - return extracted.startOffset + extracted.selectionEnd; - } - - /** - * @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 = getWordRangeAtCursor(connection, separators); - return (range == null) ? null : range.word; - } - - /** - * Removes the word surrounding the cursor. Parameters are identical to - * getWordAtCursor. - */ - public static void deleteWordAtCursor( - InputConnection connection, String separators) { - - Range range = getWordRangeAtCursor(connection, separators); - if (range == null) return; - - connection.finishComposingText(); - // Move cursor to beginning of word, to avoid crash when cursor is outside - // of valid range after deleting text. - int newCursor = getCursorPosition(connection) - range.charsBefore; - connection.setSelection(newCursor, newCursor); - connection.deleteSurroundingText(0, range.charsBefore + range.charsAfter); - } - - /** - * Represents a range of text, relative to the current cursor position. - */ - private static class Range { - /** Characters before selection start */ - int charsBefore; - - /** - * Characters after selection start, including one trailing word - * separator. - */ - int charsAfter; - - /** The actual characters that make up a word */ - String word; - - public Range(int charsBefore, int charsAfter, String word) { - if (charsBefore < 0 || charsAfter < 0) { - throw new IndexOutOfBoundsException(); - } - this.charsBefore = charsBefore; - this.charsAfter = charsAfter; - this.word = word; - } - } - - private static Range getWordRangeAtCursor( - InputConnection connection, String sep) { - if (connection == null || sep == null) { - return null; - } - CharSequence before = connection.getTextBeforeCursor(1000, 0); - CharSequence after = connection.getTextAfterCursor(1000, 0); - if (before == null || after == null) { - return null; - } - - // Find first word separator before the cursor - int start = before.length(); - while (--start > 0 && !isWhitespace(before.charAt(start - 1), sep)); - - // 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); - } - - return null; - } - - private static boolean isWhitespace(int code, String whitespace) { - return whitespace.contains(String.valueOf((char) code)); - } -} 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/android/inputmethod/voice/VoiceInput.java b/java/src/com/android/inputmethod/voice/VoiceInput.java index ac06ab50d..f24c180d0 100644 --- a/java/src/com/android/inputmethod/voice/VoiceInput.java +++ b/java/src/com/android/inputmethod/voice/VoiceInput.java @@ -25,6 +25,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.os.Parcelable; import android.speech.RecognitionListener; import android.speech.SpeechRecognizer; import android.speech.RecognizerIntent; @@ -52,6 +53,8 @@ public class VoiceInput implements OnClickListener { private static final String EXTRA_RECOGNITION_CONTEXT = "android.speech.extras.RECOGNITION_CONTEXT"; private static final String EXTRA_CALLING_PACKAGE = "calling_package"; + private static final String EXTRA_ALTERNATES = "android.speech.extra.ALTERNATES"; + private static final int MAX_ALT_LIST_LENGTH = 6; private static final String DEFAULT_RECOMMENDED_PACKAGES = "com.android.mms " + @@ -63,7 +66,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 @@ -73,6 +76,25 @@ public class VoiceInput implements OnClickListener { private VoiceInputLogger mLogger; + // Names of a few extras defined in VoiceSearch's RecognitionController + // Note, the version of voicesearch that shipped in Froyo returns the raw + // RecognitionClientAlternates protocol buffer under the key "alternates", + // so a VS market update must be installed on Froyo devices in order to see + // alternatives. + private static final String ALTERNATES_BUNDLE = "alternates_bundle"; + + // This is copied from the VoiceSearch app. + private static final class AlternatesBundleKeys { + public static final String ALTERNATES = "alternates"; + public static final String CONFIDENCE = "confidence"; + public static final String LENGTH = "length"; + public static final String MAX_SPAN_LENGTH = "max_span_length"; + public static final String SPANS = "spans"; + public static final String SPAN_KEY_DELIMITER = ":"; + public static final String START = "start"; + public static final String TEXT = "text"; + } + // Names of a few intent extras defined in VoiceSearch's RecognitionService. // These let us tweak the endpointer parameters. private static final String EXTRA_SPEECH_MINIMUM_LENGTH_MILLIS = @@ -304,12 +326,12 @@ public class VoiceInput implements OnClickListener { intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, ""); intent.putExtra(EXTRA_RECOGNITION_CONTEXT, context.getBundle()); intent.putExtra(EXTRA_CALLING_PACKAGE, "VoiceIME"); + intent.putExtra(EXTRA_ALTERNATES, true); intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, SettingsUtil.getSettingsInt( mContext.getContentResolver(), SettingsUtil.LATIN_IME_MAX_VOICE_RESULTS, 1)); - // Get endpointer params from Gservices. // TODO: Consider caching these values for improved performance on slower devices. final ContentResolver cr = mContext.getContentResolver(); @@ -563,26 +585,42 @@ public class VoiceInput implements OnClickListener { public void onResults(Bundle resultsBundle) { List<String> results = resultsBundle .getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); + // VS Market update is needed for IME froyo clients to access the alternatesBundle + // TODO: verify this. + Bundle alternatesBundle = resultsBundle.getBundle(ALTERNATES_BUNDLE); mState = DEFAULT; final Map<String, List<CharSequence>> alternatives = - new HashMap<String, List<CharSequence>>(); - if (results.size() >= 2 && ENABLE_WORD_CORRECTIONS) { - final String[][] words = new String[results.size()][]; - for (int i = 0; i < words.length; i++) { - words[i] = results.get(i).split(" "); - } - - for (int key = 0; key < words[0].length; key++) { - alternatives.put(words[0][key], new ArrayList<CharSequence>()); - for (int alt = 1; alt < words.length; alt++) { - int keyBegin = key * words[alt].length / words[0].length; - int keyEnd = (key + 1) * words[alt].length / words[0].length; - - for (int i = keyBegin; i < Math.min(words[alt].length, keyEnd); i++) { - List<CharSequence> altList = alternatives.get(words[0][key]); - if (!altList.contains(words[alt][i]) && altList.size() < 6) { - altList.add(words[alt][i]); + new HashMap<String, List<CharSequence>>(); + + if (ENABLE_WORD_CORRECTIONS && alternatesBundle != null && results.size() > 0) { + // Use the top recognition result to map each alternative's start:length to a word. + String[] words = results.get(0).split(" "); + Bundle spansBundle = alternatesBundle.getBundle(AlternatesBundleKeys.SPANS); + for (String key : spansBundle.keySet()) { + // Get the word for which these alternates correspond to. + Bundle spanBundle = spansBundle.getBundle(key); + int start = spanBundle.getInt(AlternatesBundleKeys.START); + int length = spanBundle.getInt(AlternatesBundleKeys.LENGTH); + // Only keep single-word based alternatives. + if (length == 1 && start < words.length) { + // Get the alternatives associated with the span. + // If a word appears twice in a recognition result, + // concatenate the alternatives for the word. + List<CharSequence> altList = alternatives.get(words[start]); + if (altList == null) { + altList = new ArrayList<CharSequence>(); + alternatives.put(words[start], altList); + } + Parcelable[] alternatesArr = spanBundle + .getParcelableArray(AlternatesBundleKeys.ALTERNATES); + for (int j = 0; j < alternatesArr.length && + altList.size() < MAX_ALT_LIST_LENGTH; j++) { + Bundle alternateBundle = (Bundle) alternatesArr[j]; + String alternate = alternateBundle.getString(AlternatesBundleKeys.TEXT); + // Don't allow duplicates in the alternates list. + if (!altList.contains(alternate)) { + altList.add(alternate); } } } |