Refactor to move voice functionarities in LatinIME to VoiceIMEConnector
Change-Id: I593a8187d48338c9c0e7d75c73c2dbfc32400335main
parent
04448c2978
commit
4092205833
|
@ -17,8 +17,7 @@
|
||||||
package com.android.inputmethod.latin;
|
package com.android.inputmethod.latin;
|
||||||
|
|
||||||
import com.android.inputmethod.latin.LatinIMEUtil.RingCharBuffer;
|
import com.android.inputmethod.latin.LatinIMEUtil.RingCharBuffer;
|
||||||
import com.android.inputmethod.voice.FieldContext;
|
import com.android.inputmethod.voice.VoiceIMEConnector;
|
||||||
import com.android.inputmethod.voice.VoiceInput;
|
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
@ -41,8 +40,6 @@ import android.os.SystemClock;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
import android.preference.PreferenceActivity;
|
import android.preference.PreferenceActivity;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.speech.SpeechRecognizer;
|
|
||||||
import android.text.ClipboardManager;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
@ -50,10 +47,7 @@ import android.util.PrintWriterPrinter;
|
||||||
import android.util.Printer;
|
import android.util.Printer;
|
||||||
import android.view.HapticFeedbackConstants;
|
import android.view.HapticFeedbackConstants;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.ViewParent;
|
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.view.inputmethod.CompletionInfo;
|
import android.view.inputmethod.CompletionInfo;
|
||||||
|
@ -70,25 +64,20 @@ import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input method implementation for Qwerty'ish keyboard.
|
* Input method implementation for Qwerty'ish keyboard.
|
||||||
*/
|
*/
|
||||||
public class LatinIME extends InputMethodService
|
public class LatinIME extends InputMethodService
|
||||||
implements BaseKeyboardView.OnKeyboardActionListener,
|
implements BaseKeyboardView.OnKeyboardActionListener,
|
||||||
VoiceInput.UiListener,
|
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener,
|
SharedPreferences.OnSharedPreferenceChangeListener,
|
||||||
Tutorial.TutorialListener {
|
Tutorial.TutorialListener {
|
||||||
private static final String TAG = "LatinIME";
|
private static final String TAG = "LatinIME";
|
||||||
private static final boolean PERF_DEBUG = false;
|
private static final boolean PERF_DEBUG = false;
|
||||||
static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
static final boolean TRACE = false;
|
private static final boolean TRACE = false;
|
||||||
static final boolean VOICE_INSTALLED = true;
|
|
||||||
static final boolean ENABLE_VOICE_BUTTON = true;
|
|
||||||
|
|
||||||
private static final String PREF_SOUND_ON = "sound_on";
|
private static final String PREF_SOUND_ON = "sound_on";
|
||||||
private static final String PREF_POPUP_ON = "popup_on";
|
private static final String PREF_POPUP_ON = "popup_on";
|
||||||
|
@ -97,21 +86,6 @@ public class LatinIME extends InputMethodService
|
||||||
private static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting";
|
private static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting";
|
||||||
private static final String PREF_AUTO_COMPLETION_THRESHOLD = "auto_completion_threshold";
|
private static final String PREF_AUTO_COMPLETION_THRESHOLD = "auto_completion_threshold";
|
||||||
private static final String PREF_BIGRAM_SUGGESTIONS = "bigram_suggestion";
|
private static final String PREF_BIGRAM_SUGGESTIONS = "bigram_suggestion";
|
||||||
private static final String PREF_VOICE_MODE = "voice_mode";
|
|
||||||
|
|
||||||
// Whether or not the user has used voice input before (and thus, whether to show the
|
|
||||||
// first-run warning dialog or not).
|
|
||||||
private static final String PREF_HAS_USED_VOICE_INPUT = "has_used_voice_input";
|
|
||||||
|
|
||||||
// Whether or not the user has used voice input from an unsupported locale UI before.
|
|
||||||
// For example, the user has a Chinese UI but activates voice input.
|
|
||||||
private static final String PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE =
|
|
||||||
"has_used_voice_input_unsupported_locale";
|
|
||||||
|
|
||||||
// The private IME option used to indicate that no microphone should be shown for a
|
|
||||||
// given text field. For instance this is specified by the search dialog when the
|
|
||||||
// dialog is already showing a voice search button.
|
|
||||||
private static final String IME_OPTION_NO_MICROPHONE = "nm";
|
|
||||||
|
|
||||||
public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
|
public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
|
||||||
public static final String PREF_INPUT_LANGUAGE = "input_language";
|
public static final String PREF_INPUT_LANGUAGE = "input_language";
|
||||||
|
@ -156,29 +130,22 @@ public class LatinIME extends InputMethodService
|
||||||
private CompletionInfo[] mCompletions;
|
private CompletionInfo[] mCompletions;
|
||||||
|
|
||||||
private AlertDialog mOptionsDialog;
|
private AlertDialog mOptionsDialog;
|
||||||
private AlertDialog mVoiceWarningDialog;
|
|
||||||
|
|
||||||
private KeyboardSwitcher mKeyboardSwitcher;
|
private KeyboardSwitcher mKeyboardSwitcher;
|
||||||
private SubtypeSwitcher mSubtypeSwitcher;
|
private SubtypeSwitcher mSubtypeSwitcher;
|
||||||
|
private VoiceIMEConnector mVoiceConnector;
|
||||||
|
|
||||||
private UserDictionary mUserDictionary;
|
private UserDictionary mUserDictionary;
|
||||||
private UserBigramDictionary mUserBigramDictionary;
|
private UserBigramDictionary mUserBigramDictionary;
|
||||||
private ContactsDictionary mContactsDictionary;
|
private ContactsDictionary mContactsDictionary;
|
||||||
private AutoDictionary mAutoDictionary;
|
private AutoDictionary mAutoDictionary;
|
||||||
|
|
||||||
private Hints mHints;
|
|
||||||
|
|
||||||
private Resources mResources;
|
private Resources mResources;
|
||||||
|
|
||||||
private final StringBuilder mComposing = new StringBuilder();
|
private final StringBuilder mComposing = new StringBuilder();
|
||||||
private WordComposer mWord = new WordComposer();
|
private WordComposer mWord = new WordComposer();
|
||||||
private int mCommittedLength;
|
private int mCommittedLength;
|
||||||
private boolean mPredicting;
|
private boolean mPredicting;
|
||||||
private boolean mRecognizing;
|
|
||||||
private boolean mAfterVoiceInput;
|
|
||||||
private boolean mImmediatelyAfterVoiceInput;
|
|
||||||
private boolean mShowingVoiceSuggestions;
|
|
||||||
private boolean mVoiceInputHighlighted;
|
|
||||||
private CharSequence mBestWord;
|
private CharSequence mBestWord;
|
||||||
private boolean mPredictionOn;
|
private boolean mPredictionOn;
|
||||||
private boolean mCompletionOn;
|
private boolean mCompletionOn;
|
||||||
|
@ -189,19 +156,13 @@ public class LatinIME extends InputMethodService
|
||||||
private boolean mReCorrectionEnabled;
|
private boolean mReCorrectionEnabled;
|
||||||
private boolean mBigramSuggestionEnabled;
|
private boolean mBigramSuggestionEnabled;
|
||||||
private boolean mAutoCorrectOn;
|
private boolean mAutoCorrectOn;
|
||||||
private boolean mPasswordText;
|
|
||||||
private boolean mVibrateOn;
|
private boolean mVibrateOn;
|
||||||
private boolean mSoundOn;
|
private boolean mSoundOn;
|
||||||
private boolean mPopupOn;
|
private boolean mPopupOn;
|
||||||
private boolean mAutoCap;
|
private boolean mAutoCap;
|
||||||
private boolean mQuickFixes;
|
private boolean mQuickFixes;
|
||||||
private boolean mHasUsedVoiceInput;
|
|
||||||
private boolean mHasUsedVoiceInputUnsupportedLocale;
|
|
||||||
private boolean mLocaleSupportedForVoiceInput;
|
|
||||||
private boolean mIsShowingHint;
|
|
||||||
private int mCorrectionMode;
|
private int mCorrectionMode;
|
||||||
private boolean mVoiceKeyEnabled;
|
|
||||||
private boolean mVoiceButtonOnPrimary;
|
|
||||||
private int mOrientation;
|
private int mOrientation;
|
||||||
private List<CharSequence> mSuggestPuncList;
|
private List<CharSequence> mSuggestPuncList;
|
||||||
// Keep track of the last selection range to decide if we need to show word alternatives
|
// Keep track of the last selection range to decide if we need to show word alternatives
|
||||||
|
@ -227,25 +188,15 @@ public class LatinIME extends InputMethodService
|
||||||
/* package */ String mWordSeparators;
|
/* package */ String mWordSeparators;
|
||||||
private String mSentenceSeparators;
|
private String mSentenceSeparators;
|
||||||
private String mSuggestPuncs;
|
private String mSuggestPuncs;
|
||||||
private VoiceInput mVoiceInput;
|
// TODO: Move this flag to VoiceIMEConnector
|
||||||
private final VoiceResults mVoiceResults = new VoiceResults();
|
|
||||||
private boolean mConfigurationChanging;
|
private boolean mConfigurationChanging;
|
||||||
|
|
||||||
// Keeps track of most recently inserted text (multi-character key) for reverting
|
// Keeps track of most recently inserted text (multi-character key) for reverting
|
||||||
private CharSequence mEnteredText;
|
private CharSequence mEnteredText;
|
||||||
private boolean mRefreshKeyboardRequired;
|
private boolean mRefreshKeyboardRequired;
|
||||||
|
|
||||||
// For each word, a list of potential replacements, usually from voice.
|
|
||||||
private final Map<String, List<CharSequence>> mWordToSuggestions =
|
|
||||||
new HashMap<String, List<CharSequence>>();
|
|
||||||
|
|
||||||
private final ArrayList<WordAlternatives> mWordHistory = new ArrayList<WordAlternatives>();
|
private final ArrayList<WordAlternatives> mWordHistory = new ArrayList<WordAlternatives>();
|
||||||
|
|
||||||
private class VoiceResults {
|
|
||||||
List<String> candidates;
|
|
||||||
Map<String, List<CharSequence>> alternatives;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract static class WordAlternatives {
|
public abstract static class WordAlternatives {
|
||||||
protected CharSequence mChosenWord;
|
protected CharSequence mChosenWord;
|
||||||
|
|
||||||
|
@ -294,9 +245,9 @@ public class LatinIME extends InputMethodService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ UIHandler mHandler = new UIHandler();
|
/* package */ final UIHandler mHandler = new UIHandler();
|
||||||
|
|
||||||
/* package */ class UIHandler extends Handler {
|
public class UIHandler extends Handler {
|
||||||
private static final int MSG_UPDATE_SUGGESTIONS = 0;
|
private static final int MSG_UPDATE_SUGGESTIONS = 0;
|
||||||
private static final int MSG_UPDATE_OLD_SUGGESTIONS = 1;
|
private static final int MSG_UPDATE_OLD_SUGGESTIONS = 1;
|
||||||
private static final int MSG_UPDATE_SHIFT_STATE = 2;
|
private static final int MSG_UPDATE_SHIFT_STATE = 2;
|
||||||
|
@ -316,7 +267,9 @@ public class LatinIME extends InputMethodService
|
||||||
mKeyboardSwitcher.updateShiftState();
|
mKeyboardSwitcher.updateShiftState();
|
||||||
break;
|
break;
|
||||||
case MSG_VOICE_RESULTS:
|
case MSG_VOICE_RESULTS:
|
||||||
handleVoiceResults();
|
mVoiceConnector.handleVoiceResults(mKeyboardSwitcher, preferCapitalization()
|
||||||
|
|| (mKeyboardSwitcher.isAlphabetMode()
|
||||||
|
&& mKeyboardSwitcher.isShiftedOrShiftLocked()));
|
||||||
break;
|
break;
|
||||||
case MSG_START_TUTORIAL:
|
case MSG_START_TUTORIAL:
|
||||||
if (mTutorial == null) {
|
if (mTutorial == null) {
|
||||||
|
@ -405,19 +358,7 @@ public class LatinIME extends InputMethodService
|
||||||
// register to receive ringer mode changes for silent mode
|
// register to receive ringer mode changes for silent mode
|
||||||
IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
|
IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
|
||||||
registerReceiver(mReceiver, filter);
|
registerReceiver(mReceiver, filter);
|
||||||
if (VOICE_INSTALLED) {
|
mVoiceConnector = VoiceIMEConnector.init(this, mHandler);
|
||||||
mVoiceInput = new VoiceInput(this, this);
|
|
||||||
mHints = new Hints(this, new Hints.Display() {
|
|
||||||
public void showHint(int viewResource) {
|
|
||||||
LayoutInflater inflater = (LayoutInflater) getSystemService(
|
|
||||||
Context.LAYOUT_INFLATER_SERVICE);
|
|
||||||
View view = inflater.inflate(viewResource, null);
|
|
||||||
setCandidatesView(view);
|
|
||||||
setCandidatesViewShown(true);
|
|
||||||
mIsShowingHint = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
prefs.registerOnSharedPreferenceChangeListener(this);
|
prefs.registerOnSharedPreferenceChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,9 +449,7 @@ public class LatinIME extends InputMethodService
|
||||||
mContactsDictionary.close();
|
mContactsDictionary.close();
|
||||||
}
|
}
|
||||||
unregisterReceiver(mReceiver);
|
unregisterReceiver(mReceiver);
|
||||||
if (VOICE_INSTALLED && mVoiceInput != null) {
|
mVoiceConnector.destroy();
|
||||||
mVoiceInput.destroy();
|
|
||||||
}
|
|
||||||
LatinImeLogger.commit();
|
LatinImeLogger.commit();
|
||||||
LatinImeLogger.onDestroy();
|
LatinImeLogger.onDestroy();
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
@ -531,15 +470,14 @@ public class LatinIME extends InputMethodService
|
||||||
final int mode = mKeyboardSwitcher.getKeyboardMode();
|
final int mode = mKeyboardSwitcher.getKeyboardMode();
|
||||||
final EditorInfo attribute = getCurrentInputEditorInfo();
|
final EditorInfo attribute = getCurrentInputEditorInfo();
|
||||||
final int imeOptions = (attribute != null) ? attribute.imeOptions : 0;
|
final int imeOptions = (attribute != null) ? attribute.imeOptions : 0;
|
||||||
mKeyboardSwitcher.loadKeyboard(mode, imeOptions, mVoiceKeyEnabled,
|
mKeyboardSwitcher.loadKeyboard(mode, imeOptions,
|
||||||
mVoiceButtonOnPrimary);
|
mVoiceConnector.isVoiceButtonEnabled(),
|
||||||
|
mVoiceConnector.isVoiceButtonOnPrimary());
|
||||||
}
|
}
|
||||||
|
|
||||||
mConfigurationChanging = true;
|
mConfigurationChanging = true;
|
||||||
super.onConfigurationChanged(conf);
|
super.onConfigurationChanged(conf);
|
||||||
if (mRecognizing) {
|
mVoiceConnector.onConfigurationChanged(mConfigurationChanging);
|
||||||
switchToRecognitionStatusView();
|
|
||||||
}
|
|
||||||
mConfigurationChanging = false;
|
mConfigurationChanging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,16 +529,8 @@ public class LatinIME extends InputMethodService
|
||||||
// Most such things we decide below in the switch statement, but we need to know
|
// Most such things we decide below in the switch statement, but we need to know
|
||||||
// now whether this is a password text field, because we need to know now (before
|
// now whether this is a password text field, because we need to know now (before
|
||||||
// the switch statement) whether we want to enable the voice button.
|
// the switch statement) whether we want to enable the voice button.
|
||||||
mPasswordText = false;
|
|
||||||
int variation = attribute.inputType & EditorInfo.TYPE_MASK_VARIATION;
|
int variation = attribute.inputType & EditorInfo.TYPE_MASK_VARIATION;
|
||||||
if (isPasswordVariation(variation)) {
|
mVoiceConnector.resetVoiceStates(isPasswordVariation(variation));
|
||||||
mPasswordText = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mAfterVoiceInput = false;
|
|
||||||
mImmediatelyAfterVoiceInput = false;
|
|
||||||
mShowingVoiceSuggestions = false;
|
|
||||||
mVoiceInputHighlighted = false;
|
|
||||||
mInputTypeNoAutoCorrect = false;
|
mInputTypeNoAutoCorrect = false;
|
||||||
mPredictionOn = false;
|
mPredictionOn = false;
|
||||||
mCompletionOn = false;
|
mCompletionOn = false;
|
||||||
|
@ -679,7 +609,8 @@ public class LatinIME extends InputMethodService
|
||||||
mJustAddedAutoSpace = false;
|
mJustAddedAutoSpace = false;
|
||||||
|
|
||||||
loadSettings(attribute);
|
loadSettings(attribute);
|
||||||
switcher.loadKeyboard(mode, attribute.imeOptions, mVoiceKeyEnabled, mVoiceButtonOnPrimary);
|
switcher.loadKeyboard(mode, attribute.imeOptions, mVoiceConnector.isVoiceButtonEnabled(),
|
||||||
|
mVoiceConnector.isVoiceButtonOnPrimary());
|
||||||
switcher.updateShiftState();
|
switcher.updateShiftState();
|
||||||
|
|
||||||
setCandidatesViewShownInternal(isCandidateStripVisible(),
|
setCandidatesViewShownInternal(isCandidateStripVisible(),
|
||||||
|
@ -731,14 +662,8 @@ public class LatinIME extends InputMethodService
|
||||||
LatinImeLogger.commit();
|
LatinImeLogger.commit();
|
||||||
onAutoCompletionStateChanged(false);
|
onAutoCompletionStateChanged(false);
|
||||||
|
|
||||||
if (VOICE_INSTALLED && !mConfigurationChanging) {
|
mVoiceConnector.flushVoiceInputLogs(mConfigurationChanging);
|
||||||
if (mAfterVoiceInput) {
|
|
||||||
mVoiceInput.flushAllTextModificationCounters();
|
|
||||||
mVoiceInput.logInputEnded();
|
|
||||||
}
|
|
||||||
mVoiceInput.flushLogs();
|
|
||||||
mVoiceInput.cancel();
|
|
||||||
}
|
|
||||||
BaseKeyboardView inputView = mKeyboardSwitcher.getInputView();
|
BaseKeyboardView inputView = mKeyboardSwitcher.getInputView();
|
||||||
if (inputView != null)
|
if (inputView != null)
|
||||||
inputView.closing();
|
inputView.closing();
|
||||||
|
@ -760,13 +685,7 @@ public class LatinIME extends InputMethodService
|
||||||
@Override
|
@Override
|
||||||
public void onUpdateExtractedText(int token, ExtractedText text) {
|
public void onUpdateExtractedText(int token, ExtractedText text) {
|
||||||
super.onUpdateExtractedText(token, text);
|
super.onUpdateExtractedText(token, text);
|
||||||
InputConnection ic = getCurrentInputConnection();
|
mVoiceConnector.showPunctuationHintIfNecessary();
|
||||||
if (!mImmediatelyAfterVoiceInput && mAfterVoiceInput && ic != null) {
|
|
||||||
if (mHints.showPunctuationHintIfNecessary(ic)) {
|
|
||||||
mVoiceInput.logPunctuationHintDisplayed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mImmediatelyAfterVoiceInput = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -785,14 +704,12 @@ public class LatinIME extends InputMethodService
|
||||||
+ ", ce=" + candidatesEnd);
|
+ ", ce=" + candidatesEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mAfterVoiceInput) {
|
mVoiceConnector.setCursorAndSelection(newSelEnd, newSelStart);
|
||||||
mVoiceInput.setCursorPos(newSelEnd);
|
|
||||||
mVoiceInput.setSelectionSpan(newSelEnd - newSelStart);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the current selection in the text view changes, we should
|
// If the current selection in the text view changes, we should
|
||||||
// clear whatever candidate text we have.
|
// clear whatever candidate text we have.
|
||||||
if ((((mComposing.length() > 0 && mPredicting) || mVoiceInputHighlighted)
|
if ((((mComposing.length() > 0 && mPredicting)
|
||||||
|
|| mVoiceConnector.isVoiceInputHighlighted())
|
||||||
&& (newSelStart != candidatesEnd
|
&& (newSelStart != candidatesEnd
|
||||||
|| newSelEnd != candidatesEnd)
|
|| newSelEnd != candidatesEnd)
|
||||||
&& mLastSelectionStart != newSelStart)) {
|
&& mLastSelectionStart != newSelStart)) {
|
||||||
|
@ -804,7 +721,7 @@ public class LatinIME extends InputMethodService
|
||||||
if (ic != null) {
|
if (ic != null) {
|
||||||
ic.finishComposingText();
|
ic.finishComposingText();
|
||||||
}
|
}
|
||||||
mVoiceInputHighlighted = false;
|
mVoiceConnector.setVoiceInputHighlighted(false);
|
||||||
} else if (!mPredicting && !mJustAccepted) {
|
} else if (!mPredicting && !mJustAccepted) {
|
||||||
switch (TextEntryState.getState()) {
|
switch (TextEntryState.getState()) {
|
||||||
case ACCEPTED_DEFAULT:
|
case ACCEPTED_DEFAULT:
|
||||||
|
@ -829,8 +746,7 @@ public class LatinIME extends InputMethodService
|
||||||
if (isPredictionOn() && !mJustReverted
|
if (isPredictionOn() && !mJustReverted
|
||||||
&& (candidatesStart == candidatesEnd || newSelStart != oldSelStart
|
&& (candidatesStart == candidatesEnd || newSelStart != oldSelStart
|
||||||
|| TextEntryState.isCorrecting())
|
|| TextEntryState.isCorrecting())
|
||||||
&& (newSelStart < newSelEnd - 1 || (!mPredicting))
|
&& (newSelStart < newSelEnd - 1 || (!mPredicting))) {
|
||||||
&& !mVoiceInputHighlighted) {
|
|
||||||
if (isCursorTouchingWord() || mLastSelectionStart < mLastSelectionEnd) {
|
if (isCursorTouchingWord() || mLastSelectionStart < mLastSelectionEnd) {
|
||||||
mHandler.postUpdateOldSuggestions();
|
mHandler.postUpdateOldSuggestions();
|
||||||
} else {
|
} else {
|
||||||
|
@ -888,18 +804,7 @@ public class LatinIME extends InputMethodService
|
||||||
mOptionsDialog.dismiss();
|
mOptionsDialog.dismiss();
|
||||||
mOptionsDialog = null;
|
mOptionsDialog = null;
|
||||||
}
|
}
|
||||||
if (!mConfigurationChanging) {
|
mVoiceConnector.hideVoiceWindow(mConfigurationChanging);
|
||||||
if (mAfterVoiceInput) mVoiceInput.logInputEnded();
|
|
||||||
if (mVoiceWarningDialog != null && mVoiceWarningDialog.isShowing()) {
|
|
||||||
mVoiceInput.logKeyboardWarningDialogDismissed();
|
|
||||||
mVoiceWarningDialog.dismiss();
|
|
||||||
mVoiceWarningDialog = null;
|
|
||||||
}
|
|
||||||
if (VOICE_INSTALLED & mRecognizing) {
|
|
||||||
mVoiceInput.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mWordToSuggestions.clear();
|
|
||||||
mWordHistory.clear();
|
mWordHistory.clear();
|
||||||
super.hideWindow();
|
super.hideWindow();
|
||||||
TextEntryState.endSession();
|
TextEntryState.endSession();
|
||||||
|
@ -1019,21 +924,7 @@ public class LatinIME extends InputMethodService
|
||||||
return super.onKeyUp(keyCode, event);
|
return super.onKeyUp(keyCode, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void revertVoiceInput() {
|
public void commitTyped(InputConnection inputConnection) {
|
||||||
InputConnection ic = getCurrentInputConnection();
|
|
||||||
if (ic != null) ic.commitText("", 1);
|
|
||||||
updateSuggestions();
|
|
||||||
mVoiceInputHighlighted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void commitVoiceInput() {
|
|
||||||
InputConnection ic = getCurrentInputConnection();
|
|
||||||
if (ic != null) ic.finishComposingText();
|
|
||||||
updateSuggestions();
|
|
||||||
mVoiceInputHighlighted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void commitTyped(InputConnection inputConnection) {
|
|
||||||
if (mPredicting) {
|
if (mPredicting) {
|
||||||
mPredicting = false;
|
mPredicting = false;
|
||||||
if (mComposing.length() > 0) {
|
if (mComposing.length() > 0) {
|
||||||
|
@ -1222,10 +1113,9 @@ public class LatinIME extends InputMethodService
|
||||||
case LatinKeyboardView.KEYCODE_CAPSLOCK:
|
case LatinKeyboardView.KEYCODE_CAPSLOCK:
|
||||||
switcher.toggleCapsLock();
|
switcher.toggleCapsLock();
|
||||||
break;
|
break;
|
||||||
case LatinKeyboardView.KEYCODE_VOICE:
|
case LatinKeyboardView.KEYCODE_VOICE: /* was a button press, was not a swipe */
|
||||||
if (VOICE_INSTALLED) {
|
mVoiceConnector.startListening(false,
|
||||||
startListening(false /* was a button press, was not a swipe */);
|
mKeyboardSwitcher.getInputView().getWindowToken(), mConfigurationChanging);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case KEYCODE_TAB:
|
case KEYCODE_TAB:
|
||||||
handleTab();
|
handleTab();
|
||||||
|
@ -1250,9 +1140,7 @@ public class LatinIME extends InputMethodService
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onText(CharSequence text) {
|
public void onText(CharSequence text) {
|
||||||
if (VOICE_INSTALLED && mVoiceInputHighlighted) {
|
mVoiceConnector.commitVoiceInput();
|
||||||
commitVoiceInput();
|
|
||||||
}
|
|
||||||
InputConnection ic = getCurrentInputConnection();
|
InputConnection ic = getCurrentInputConnection();
|
||||||
if (ic == null) return;
|
if (ic == null) return;
|
||||||
abortCorrection(false);
|
abortCorrection(false);
|
||||||
|
@ -1274,29 +1162,14 @@ public class LatinIME extends InputMethodService
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBackspace() {
|
private void handleBackspace() {
|
||||||
if (VOICE_INSTALLED && mVoiceInputHighlighted) {
|
if (mVoiceConnector.logAndRevertVoiceInput()) return;
|
||||||
mVoiceInput.incrementTextModificationDeleteCount(
|
|
||||||
mVoiceResults.candidates.get(0).toString().length());
|
|
||||||
revertVoiceInput();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
boolean deleteChar = false;
|
boolean deleteChar = false;
|
||||||
InputConnection ic = getCurrentInputConnection();
|
InputConnection ic = getCurrentInputConnection();
|
||||||
if (ic == null) return;
|
if (ic == null) return;
|
||||||
|
|
||||||
ic.beginBatchEdit();
|
ic.beginBatchEdit();
|
||||||
|
|
||||||
if (mAfterVoiceInput) {
|
mVoiceConnector.handleBackspace();
|
||||||
// Don't log delete if the user is pressing delete at
|
|
||||||
// the beginning of the text box (hence not deleting anything)
|
|
||||||
if (mVoiceInput.getCursorPos() > 0) {
|
|
||||||
// If anything was selected before the delete was pressed, increment the
|
|
||||||
// delete count by the length of the selection
|
|
||||||
int deleteLen = mVoiceInput.getSelectionSpan() > 0 ?
|
|
||||||
mVoiceInput.getSelectionSpan() : 1;
|
|
||||||
mVoiceInput.incrementTextModificationDeleteCount(deleteLen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mPredicting) {
|
if (mPredicting) {
|
||||||
final int length = mComposing.length();
|
final int length = mComposing.length();
|
||||||
|
@ -1377,14 +1250,8 @@ public class LatinIME extends InputMethodService
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleCharacter(int primaryCode, int[] keyCodes) {
|
private void handleCharacter(int primaryCode, int[] keyCodes) {
|
||||||
if (VOICE_INSTALLED && mVoiceInputHighlighted) {
|
mVoiceConnector.handleCharacter();
|
||||||
commitVoiceInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mAfterVoiceInput) {
|
|
||||||
// Assume input length is 1. This assumption fails for smiley face insertions.
|
|
||||||
mVoiceInput.incrementTextModificationInsertCount(1);
|
|
||||||
}
|
|
||||||
if (mLastSelectionStart == mLastSelectionEnd && TextEntryState.isCorrecting()) {
|
if (mLastSelectionStart == mLastSelectionEnd && TextEntryState.isCorrecting()) {
|
||||||
abortCorrection(false);
|
abortCorrection(false);
|
||||||
}
|
}
|
||||||
|
@ -1441,14 +1308,7 @@ public class LatinIME extends InputMethodService
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSeparator(int primaryCode) {
|
private void handleSeparator(int primaryCode) {
|
||||||
if (VOICE_INSTALLED && mVoiceInputHighlighted) {
|
mVoiceConnector.handleSeparator();
|
||||||
commitVoiceInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mAfterVoiceInput){
|
|
||||||
// Assume input length is 1. This assumption fails for smiley face insertions.
|
|
||||||
mVoiceInput.incrementTextModificationInsertPunctuationCount(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should dismiss the "Touch again to save" message when handling separator
|
// Should dismiss the "Touch again to save" message when handling separator
|
||||||
if (mCandidateView != null && mCandidateView.dismissAddToDictionaryHint()) {
|
if (mCandidateView != null && mCandidateView.dismissAddToDictionaryHint()) {
|
||||||
|
@ -1509,9 +1369,7 @@ public class LatinIME extends InputMethodService
|
||||||
|
|
||||||
private void handleClose() {
|
private void handleClose() {
|
||||||
commitTyped(getCurrentInputConnection());
|
commitTyped(getCurrentInputConnection());
|
||||||
if (VOICE_INSTALLED & mRecognizing) {
|
mVoiceConnector.handleClose();
|
||||||
mVoiceInput.cancel();
|
|
||||||
}
|
|
||||||
requestHideSelf(0);
|
requestHideSelf(0);
|
||||||
LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
|
LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
|
||||||
if (inputView != null)
|
if (inputView != null)
|
||||||
|
@ -1557,16 +1415,9 @@ public class LatinIME extends InputMethodService
|
||||||
&& (isPredictionOn() || mCompletionOn || isShowingPunctuationList()));
|
&& (isPredictionOn() || mCompletionOn || isShowingPunctuationList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCancelVoice() {
|
public void switchToKeyboardView() {
|
||||||
if (mRecognizing) {
|
|
||||||
switchToKeyboardView();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void switchToKeyboardView() {
|
|
||||||
mHandler.post(new Runnable() {
|
mHandler.post(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
mRecognizing = false;
|
|
||||||
if (mKeyboardSwitcher.getInputView() != null) {
|
if (mKeyboardSwitcher.getInputView() != null) {
|
||||||
setInputView(mKeyboardSwitcher.getInputView());
|
setInputView(mKeyboardSwitcher.getInputView());
|
||||||
}
|
}
|
||||||
|
@ -1576,175 +1427,18 @@ public class LatinIME extends InputMethodService
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void switchToRecognitionStatusView() {
|
public void clearSuggestions() {
|
||||||
final boolean configChanged = mConfigurationChanging;
|
|
||||||
mHandler.post(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
setCandidatesViewShown(false);
|
|
||||||
mRecognizing = true;
|
|
||||||
View v = mVoiceInput.getView();
|
|
||||||
ViewParent p = v.getParent();
|
|
||||||
if (p != null && p instanceof ViewGroup) {
|
|
||||||
((ViewGroup)v.getParent()).removeView(v);
|
|
||||||
}
|
|
||||||
setInputView(v);
|
|
||||||
updateInputViewShown();
|
|
||||||
if (configChanged) {
|
|
||||||
mVoiceInput.onConfigurationChanged();
|
|
||||||
}
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startListening(boolean swipe) {
|
|
||||||
if (!mHasUsedVoiceInput ||
|
|
||||||
(!mLocaleSupportedForVoiceInput && !mHasUsedVoiceInputUnsupportedLocale)) {
|
|
||||||
// Calls reallyStartListening if user clicks OK, does nothing if user clicks Cancel.
|
|
||||||
showVoiceWarningDialog(swipe);
|
|
||||||
} else {
|
|
||||||
reallyStartListening(swipe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reallyStartListening(boolean swipe) {
|
|
||||||
if (!mHasUsedVoiceInput) {
|
|
||||||
// The user has started a voice input, so remember that in the
|
|
||||||
// future (so we don't show the warning dialog after the first run).
|
|
||||||
SharedPreferences.Editor editor =
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(this).edit();
|
|
||||||
editor.putBoolean(PREF_HAS_USED_VOICE_INPUT, true);
|
|
||||||
SharedPreferencesCompat.apply(editor);
|
|
||||||
mHasUsedVoiceInput = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mLocaleSupportedForVoiceInput && !mHasUsedVoiceInputUnsupportedLocale) {
|
|
||||||
// The user has started a voice input from an unsupported locale, so remember that
|
|
||||||
// in the future (so we don't show the warning dialog the next time they do this).
|
|
||||||
SharedPreferences.Editor editor =
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(this).edit();
|
|
||||||
editor.putBoolean(PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE, true);
|
|
||||||
SharedPreferencesCompat.apply(editor);
|
|
||||||
mHasUsedVoiceInputUnsupportedLocale = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear N-best suggestions
|
|
||||||
clearSuggestions();
|
|
||||||
|
|
||||||
FieldContext context = makeFieldContext();
|
|
||||||
mVoiceInput.startListening(context, swipe);
|
|
||||||
switchToRecognitionStatusView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showVoiceWarningDialog(final boolean swipe) {
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
||||||
builder.setCancelable(true);
|
|
||||||
builder.setIcon(R.drawable.ic_mic_dialog);
|
|
||||||
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int whichButton) {
|
|
||||||
mVoiceInput.logKeyboardWarningDialogOk();
|
|
||||||
reallyStartListening(swipe);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int whichButton) {
|
|
||||||
mVoiceInput.logKeyboardWarningDialogCancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (mLocaleSupportedForVoiceInput) {
|
|
||||||
String message = getString(R.string.voice_warning_may_not_understand) + "\n\n" +
|
|
||||||
getString(R.string.voice_warning_how_to_turn_off);
|
|
||||||
builder.setMessage(message);
|
|
||||||
} else {
|
|
||||||
String message = getString(R.string.voice_warning_locale_not_supported) + "\n\n" +
|
|
||||||
getString(R.string.voice_warning_may_not_understand) + "\n\n" +
|
|
||||||
getString(R.string.voice_warning_how_to_turn_off);
|
|
||||||
builder.setMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.setTitle(R.string.voice_warning_title);
|
|
||||||
mVoiceWarningDialog = builder.create();
|
|
||||||
|
|
||||||
Window window = mVoiceWarningDialog.getWindow();
|
|
||||||
WindowManager.LayoutParams lp = window.getAttributes();
|
|
||||||
lp.token = mKeyboardSwitcher.getInputView().getWindowToken();
|
|
||||||
lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
|
|
||||||
window.setAttributes(lp);
|
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
|
|
||||||
mVoiceInput.logKeyboardWarningDialogShown();
|
|
||||||
mVoiceWarningDialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onVoiceResults(List<String> candidates,
|
|
||||||
Map<String, List<CharSequence>> alternatives) {
|
|
||||||
if (!mRecognizing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mVoiceResults.candidates = candidates;
|
|
||||||
mVoiceResults.alternatives = alternatives;
|
|
||||||
mHandler.updateVoiceResults();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleVoiceResults() {
|
|
||||||
mAfterVoiceInput = true;
|
|
||||||
mImmediatelyAfterVoiceInput = true;
|
|
||||||
|
|
||||||
InputConnection ic = getCurrentInputConnection();
|
|
||||||
if (!isFullscreenMode()) {
|
|
||||||
// Start listening for updates to the text from typing, etc.
|
|
||||||
if (ic != null) {
|
|
||||||
ExtractedTextRequest req = new ExtractedTextRequest();
|
|
||||||
ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vibrate();
|
|
||||||
switchToKeyboardView();
|
|
||||||
|
|
||||||
final List<CharSequence> nBest = new ArrayList<CharSequence>();
|
|
||||||
KeyboardSwitcher switcher = mKeyboardSwitcher;
|
|
||||||
boolean capitalizeFirstWord = preferCapitalization()
|
|
||||||
|| (switcher.isAlphabetMode() && switcher.isShiftedOrShiftLocked());
|
|
||||||
for (String c : mVoiceResults.candidates) {
|
|
||||||
if (capitalizeFirstWord) {
|
|
||||||
c = Character.toUpperCase(c.charAt(0)) + c.substring(1, c.length());
|
|
||||||
}
|
|
||||||
nBest.add(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nBest.size() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String bestResult = nBest.get(0).toString();
|
|
||||||
|
|
||||||
mVoiceInput.logVoiceInputDelivered(bestResult.length());
|
|
||||||
|
|
||||||
mHints.registerVoiceResult(bestResult);
|
|
||||||
|
|
||||||
if (ic != null) ic.beginBatchEdit(); // To avoid extra updates on committing older text
|
|
||||||
|
|
||||||
commitTyped(ic);
|
|
||||||
EditingUtil.appendText(ic, bestResult);
|
|
||||||
|
|
||||||
if (ic != null) ic.endBatchEdit();
|
|
||||||
|
|
||||||
mVoiceInputHighlighted = true;
|
|
||||||
mWordToSuggestions.putAll(mVoiceResults.alternatives);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearSuggestions() {
|
|
||||||
setSuggestions(null, false, false, false);
|
setSuggestions(null, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSuggestions(
|
public void setSuggestions(
|
||||||
List<CharSequence> suggestions,
|
List<CharSequence> suggestions,
|
||||||
boolean completions,
|
boolean completions,
|
||||||
boolean typedWordValid,
|
boolean typedWordValid,
|
||||||
boolean haveMinimalSuggestion) {
|
boolean haveMinimalSuggestion) {
|
||||||
|
|
||||||
if (mIsShowingHint) {
|
if (mVoiceConnector.getAndResetIsShowingHint()) {
|
||||||
setCandidatesView(mCandidateViewContainer);
|
setCandidatesView(mCandidateViewContainer);
|
||||||
mIsShowingHint = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCandidateView != null) {
|
if (mCandidateView != null) {
|
||||||
|
@ -1753,11 +1447,12 @@ public class LatinIME extends InputMethodService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSuggestions() {
|
public void updateSuggestions() {
|
||||||
mKeyboardSwitcher.setPreferredLetters(null);
|
mKeyboardSwitcher.setPreferredLetters(null);
|
||||||
|
|
||||||
// Check if we have a suggestion engine attached.
|
// Check if we have a suggestion engine attached.
|
||||||
if ((mSuggest == null || !isPredictionOn()) && !mVoiceInputHighlighted) {
|
if ((mSuggest == null || !isPredictionOn())
|
||||||
|
&& !mVoiceConnector.isVoiceInputHighlighted()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1846,13 +1541,7 @@ public class LatinIME extends InputMethodService
|
||||||
|
|
||||||
public void pickSuggestionManually(int index, CharSequence suggestion) {
|
public void pickSuggestionManually(int index, CharSequence suggestion) {
|
||||||
List<CharSequence> suggestions = mCandidateView.getSuggestions();
|
List<CharSequence> suggestions = mCandidateView.getSuggestions();
|
||||||
if (mAfterVoiceInput && mShowingVoiceSuggestions) {
|
mVoiceConnector.flushAndLogAllTextModificationCounters(index, suggestion, mWordSeparators);
|
||||||
mVoiceInput.flushAllTextModificationCounters();
|
|
||||||
// send this intent AFTER logging any prior aggregated edits.
|
|
||||||
mVoiceInput.logTextModifiedByChooseSuggestion(suggestion.toString(), index,
|
|
||||||
mWordSeparators,
|
|
||||||
getCurrentInputConnection());
|
|
||||||
}
|
|
||||||
|
|
||||||
final boolean correcting = TextEntryState.isCorrecting();
|
final boolean correcting = TextEntryState.isCorrecting();
|
||||||
InputConnection ic = getCurrentInputConnection();
|
InputConnection ic = getCurrentInputConnection();
|
||||||
|
@ -1932,27 +1621,6 @@ public class LatinIME extends InputMethodService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void rememberReplacedWord(CharSequence suggestion) {
|
|
||||||
if (mShowingVoiceSuggestions) {
|
|
||||||
// Retain the replaced word in the alternatives array.
|
|
||||||
EditingUtil.Range range = new EditingUtil.Range();
|
|
||||||
String wordToBeReplaced = EditingUtil.getWordAtCursor(getCurrentInputConnection(),
|
|
||||||
mWordSeparators, range);
|
|
||||||
if (!mWordToSuggestions.containsKey(wordToBeReplaced)) {
|
|
||||||
wordToBeReplaced = wordToBeReplaced.toLowerCase();
|
|
||||||
}
|
|
||||||
if (mWordToSuggestions.containsKey(wordToBeReplaced)) {
|
|
||||||
List<CharSequence> suggestions = mWordToSuggestions.get(wordToBeReplaced);
|
|
||||||
if (suggestions.contains(suggestion)) {
|
|
||||||
suggestions.remove(suggestion);
|
|
||||||
}
|
|
||||||
suggestions.add(wordToBeReplaced);
|
|
||||||
mWordToSuggestions.remove(wordToBeReplaced);
|
|
||||||
mWordToSuggestions.put(suggestion.toString(), suggestions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commits the chosen word to the text field and saves it for later
|
* Commits the chosen word to the text field and saves it for later
|
||||||
* retrieval.
|
* retrieval.
|
||||||
|
@ -1967,7 +1635,7 @@ public class LatinIME extends InputMethodService
|
||||||
return;
|
return;
|
||||||
InputConnection ic = getCurrentInputConnection();
|
InputConnection ic = getCurrentInputConnection();
|
||||||
if (ic != null) {
|
if (ic != null) {
|
||||||
rememberReplacedWord(suggestion);
|
mVoiceConnector.rememberReplacedWord(suggestion, mWordSeparators);
|
||||||
ic.commitText(suggestion, 1);
|
ic.commitText(suggestion, 1);
|
||||||
}
|
}
|
||||||
saveWordInHistory(suggestion);
|
saveWordInHistory(suggestion);
|
||||||
|
@ -1981,38 +1649,6 @@ public class LatinIME extends InputMethodService
|
||||||
switcher.updateShiftState();
|
switcher.updateShiftState();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to apply any voice alternatives for the word if this was a spoken word and
|
|
||||||
* there are voice alternatives.
|
|
||||||
* @param touching The word that the cursor is touching, with position information
|
|
||||||
* @return true if an alternative was found, false otherwise.
|
|
||||||
*/
|
|
||||||
private boolean applyVoiceAlternatives(EditingUtil.SelectedWord touching) {
|
|
||||||
// Search for result in spoken word alternatives
|
|
||||||
String selectedWord = touching.word.toString().trim();
|
|
||||||
if (!mWordToSuggestions.containsKey(selectedWord)) {
|
|
||||||
selectedWord = selectedWord.toLowerCase();
|
|
||||||
}
|
|
||||||
if (mWordToSuggestions.containsKey(selectedWord)) {
|
|
||||||
mShowingVoiceSuggestions = true;
|
|
||||||
List<CharSequence> suggestions = mWordToSuggestions.get(selectedWord);
|
|
||||||
// If the first letter of touching is capitalized, make all the suggestions
|
|
||||||
// start with a capital letter.
|
|
||||||
if (Character.isUpperCase(touching.word.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);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to apply any typed alternatives for the word if we have any cached alternatives,
|
* Tries to apply any typed alternatives for the word if we have any cached alternatives,
|
||||||
* otherwise tries to find new corrections and completions for the word.
|
* otherwise tries to find new corrections and completions for the word.
|
||||||
|
@ -2061,7 +1697,7 @@ public class LatinIME extends InputMethodService
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setOldSuggestions() {
|
private void setOldSuggestions() {
|
||||||
mShowingVoiceSuggestions = false;
|
mVoiceConnector.setShowingVoiceSuggestions(false);
|
||||||
if (mCandidateView != null && mCandidateView.isShowingAddToDictionaryHint()) {
|
if (mCandidateView != null && mCandidateView.isShowingAddToDictionaryHint()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2075,7 +1711,8 @@ public class LatinIME extends InputMethodService
|
||||||
if (touching != null && touching.word.length() > 1) {
|
if (touching != null && touching.word.length() > 1) {
|
||||||
ic.beginBatchEdit();
|
ic.beginBatchEdit();
|
||||||
|
|
||||||
if (!applyVoiceAlternatives(touching) && !applyTypedAlternatives(touching)) {
|
if (!mVoiceConnector.applyVoiceAlternatives(touching)
|
||||||
|
&& !applyTypedAlternatives(touching)) {
|
||||||
abortCorrection(true);
|
abortCorrection(true);
|
||||||
} else {
|
} else {
|
||||||
TextEntryState.selectedForCorrection();
|
TextEntryState.selectedForCorrection();
|
||||||
|
@ -2221,8 +1858,8 @@ public class LatinIME extends InputMethodService
|
||||||
final int mode = switcher.getKeyboardMode();
|
final int mode = switcher.getKeyboardMode();
|
||||||
final EditorInfo attribute = getCurrentInputEditorInfo();
|
final EditorInfo attribute = getCurrentInputEditorInfo();
|
||||||
final int imeOptions = (attribute != null) ? attribute.imeOptions : 0;
|
final int imeOptions = (attribute != null) ? attribute.imeOptions : 0;
|
||||||
switcher.loadKeyboard(mode, imeOptions, mVoiceKeyEnabled,
|
switcher.loadKeyboard(mode, imeOptions, mVoiceConnector.isVoiceButtonEnabled(),
|
||||||
mVoiceButtonOnPrimary);
|
mVoiceConnector.isVoiceButtonOnPrimary());
|
||||||
initSuggest();
|
initSuggest();
|
||||||
switcher.updateShiftState();
|
switcher.updateShiftState();
|
||||||
}
|
}
|
||||||
|
@ -2240,8 +1877,8 @@ public class LatinIME extends InputMethodService
|
||||||
|
|
||||||
public void swipeRight() {
|
public void swipeRight() {
|
||||||
if (LatinKeyboardView.DEBUG_AUTO_PLAY) {
|
if (LatinKeyboardView.DEBUG_AUTO_PLAY) {
|
||||||
ClipboardManager cm = ((ClipboardManager)getSystemService(CLIPBOARD_SERVICE));
|
CharSequence text = ((android.text.ClipboardManager)getSystemService(
|
||||||
CharSequence text = cm.getText();
|
CLIPBOARD_SERVICE)).getText();
|
||||||
if (!TextUtils.isEmpty(text)) {
|
if (!TextUtils.isEmpty(text)) {
|
||||||
mKeyboardSwitcher.getInputView().startPlaying(text.toString());
|
mKeyboardSwitcher.getInputView().startPlaying(text.toString());
|
||||||
}
|
}
|
||||||
|
@ -2284,26 +1921,6 @@ public class LatinIME extends InputMethodService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FieldContext makeFieldContext() {
|
|
||||||
return new FieldContext(
|
|
||||||
getCurrentInputConnection(),
|
|
||||||
getCurrentInputEditorInfo(),
|
|
||||||
mSubtypeSwitcher.getInputLocaleStr(),
|
|
||||||
mSubtypeSwitcher.getEnabledLanguages());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean fieldCanDoVoice(FieldContext fieldContext) {
|
|
||||||
return !mPasswordText
|
|
||||||
&& mVoiceInput != null
|
|
||||||
&& !mVoiceInput.isBlacklistedField(fieldContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldShowVoiceButton(FieldContext fieldContext, EditorInfo attribute) {
|
|
||||||
return ENABLE_VOICE_BUTTON && fieldCanDoVoice(fieldContext)
|
|
||||||
&& !(attribute != null
|
|
||||||
&& IME_OPTION_NO_MICROPHONE.equals(attribute.privateImeOptions))
|
|
||||||
&& SpeechRecognizer.isRecognitionAvailable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// receive ringer mode changes to detect silent mode
|
// receive ringer mode changes to detect silent mode
|
||||||
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||||
|
@ -2350,7 +1967,7 @@ public class LatinIME extends InputMethodService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void vibrate() {
|
public void vibrate() {
|
||||||
if (!mVibrateOn) {
|
if (!mVibrateOn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2453,24 +2070,13 @@ public class LatinIME extends InputMethodService
|
||||||
mResources.getBoolean(R.bool.default_popup_preview));
|
mResources.getBoolean(R.bool.default_popup_preview));
|
||||||
mAutoCap = sp.getBoolean(PREF_AUTO_CAP, true);
|
mAutoCap = sp.getBoolean(PREF_AUTO_CAP, true);
|
||||||
mQuickFixes = sp.getBoolean(PREF_QUICK_FIXES, true);
|
mQuickFixes = sp.getBoolean(PREF_QUICK_FIXES, true);
|
||||||
mHasUsedVoiceInput = sp.getBoolean(PREF_HAS_USED_VOICE_INPUT, false);
|
|
||||||
mHasUsedVoiceInputUnsupportedLocale =
|
|
||||||
sp.getBoolean(PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE, false);
|
|
||||||
|
|
||||||
mLocaleSupportedForVoiceInput = SubtypeSwitcher.getInstance().isVoiceSupported(
|
|
||||||
SubtypeSwitcher.getInstance().getInputLocaleStr());
|
|
||||||
|
|
||||||
mAutoCorrectEnabled = isAutoCorrectEnabled(sp);
|
mAutoCorrectEnabled = isAutoCorrectEnabled(sp);
|
||||||
mBigramSuggestionEnabled = mAutoCorrectEnabled && isBigramSuggestionEnabled(sp);
|
mBigramSuggestionEnabled = mAutoCorrectEnabled && isBigramSuggestionEnabled(sp);
|
||||||
loadAndSetAutoCompletionThreshold(sp);
|
loadAndSetAutoCompletionThreshold(sp);
|
||||||
|
|
||||||
if (VOICE_INSTALLED) {
|
mVoiceConnector.loadSettings(attribute, sp);
|
||||||
final String voiceMode = sp.getString(PREF_VOICE_MODE,
|
|
||||||
getString(R.string.voice_mode_main));
|
|
||||||
mVoiceKeyEnabled = !voiceMode.equals(getString(R.string.voice_mode_off))
|
|
||||||
&& shouldShowVoiceButton(makeFieldContext(), attribute);
|
|
||||||
mVoiceButtonOnPrimary = voiceMode.equals(getString(R.string.voice_mode_main));
|
|
||||||
}
|
|
||||||
updateCorrectionMode();
|
updateCorrectionMode();
|
||||||
updateAutoTextEnabled();
|
updateAutoTextEnabled();
|
||||||
updateSuggestionVisibility(sp);
|
updateSuggestionVisibility(sp);
|
||||||
|
|
|
@ -33,6 +33,7 @@ import android.speech.SpeechRecognizer;
|
||||||
import android.text.AutoText;
|
import android.text.AutoText;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.inputmethod.voice.VoiceIMEConnector;
|
||||||
import com.android.inputmethod.voice.VoiceInputLogger;
|
import com.android.inputmethod.voice.VoiceInputLogger;
|
||||||
|
|
||||||
public class LatinIMESettings extends PreferenceActivity
|
public class LatinIMESettings extends PreferenceActivity
|
||||||
|
@ -108,7 +109,7 @@ public class LatinIMESettings extends PreferenceActivity
|
||||||
((PreferenceGroup) findPreference(PREDICTION_SETTINGS_KEY))
|
((PreferenceGroup) findPreference(PREDICTION_SETTINGS_KEY))
|
||||||
.removePreference(mQuickFixes);
|
.removePreference(mQuickFixes);
|
||||||
}
|
}
|
||||||
if (!LatinIME.VOICE_INSTALLED
|
if (!VoiceIMEConnector.VOICE_INSTALLED
|
||||||
|| !SpeechRecognizer.isRecognitionAvailable(this)) {
|
|| !SpeechRecognizer.isRecognitionAvailable(this)) {
|
||||||
getPreferenceScreen().removePreference(mVoicePreference);
|
getPreferenceScreen().removePreference(mVoicePreference);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,537 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010 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.EditingUtil;
|
||||||
|
import com.android.inputmethod.latin.Hints;
|
||||||
|
import com.android.inputmethod.latin.KeyboardSwitcher;
|
||||||
|
import com.android.inputmethod.latin.LatinIME;
|
||||||
|
import com.android.inputmethod.latin.R;
|
||||||
|
import com.android.inputmethod.latin.SharedPreferencesCompat;
|
||||||
|
import com.android.inputmethod.latin.SubtypeSwitcher;
|
||||||
|
import com.android.inputmethod.latin.LatinIME.UIHandler;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.speech.SpeechRecognizer;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewParent;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.ExtractedTextRequest;
|
||||||
|
import android.view.inputmethod.InputConnection;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class VoiceIMEConnector implements VoiceInput.UiListener {
|
||||||
|
private static final VoiceIMEConnector sInstance = new VoiceIMEConnector();
|
||||||
|
|
||||||
|
public static final boolean VOICE_INSTALLED = true;
|
||||||
|
private static final boolean ENABLE_VOICE_BUTTON = true;
|
||||||
|
private static final String PREF_VOICE_MODE = "voice_mode";
|
||||||
|
// Whether or not the user has used voice input before (and thus, whether to show the
|
||||||
|
// first-run warning dialog or not).
|
||||||
|
private static final String PREF_HAS_USED_VOICE_INPUT = "has_used_voice_input";
|
||||||
|
// Whether or not the user has used voice input from an unsupported locale UI before.
|
||||||
|
// For example, the user has a Chinese UI but activates voice input.
|
||||||
|
private static final String PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE =
|
||||||
|
"has_used_voice_input_unsupported_locale";
|
||||||
|
// The private IME option used to indicate that no microphone should be shown for a
|
||||||
|
// given text field. For instance this is specified by the search dialog when the
|
||||||
|
// dialog is already showing a voice search button.
|
||||||
|
private static final String IME_OPTION_NO_MICROPHONE = "nm";
|
||||||
|
|
||||||
|
private boolean mAfterVoiceInput;
|
||||||
|
private boolean mHasUsedVoiceInput;
|
||||||
|
private boolean mHasUsedVoiceInputUnsupportedLocale;
|
||||||
|
private boolean mImmediatelyAfterVoiceInput;
|
||||||
|
private boolean mIsShowingHint;
|
||||||
|
private boolean mLocaleSupportedForVoiceInput;
|
||||||
|
private boolean mPasswordText;
|
||||||
|
private boolean mRecognizing;
|
||||||
|
private boolean mShowingVoiceSuggestions;
|
||||||
|
private boolean mVoiceButtonEnabled;
|
||||||
|
private boolean mVoiceButtonOnPrimary;
|
||||||
|
private boolean mVoiceInputHighlighted;
|
||||||
|
|
||||||
|
private LatinIME mContext;
|
||||||
|
private AlertDialog mVoiceWarningDialog;
|
||||||
|
private VoiceInput mVoiceInput;
|
||||||
|
private final VoiceResults mVoiceResults = new VoiceResults();
|
||||||
|
private Hints mHints;
|
||||||
|
private UIHandler mHandler;
|
||||||
|
// For each word, a list of potential replacements, usually from voice.
|
||||||
|
private final Map<String, List<CharSequence>> mWordToSuggestions =
|
||||||
|
new HashMap<String, List<CharSequence>>();
|
||||||
|
|
||||||
|
public static VoiceIMEConnector init(LatinIME context, UIHandler h) {
|
||||||
|
sInstance.initInternal(context, h);
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initInternal(LatinIME context, UIHandler h) {
|
||||||
|
mContext = context;
|
||||||
|
mHandler = h;
|
||||||
|
if (VOICE_INSTALLED) {
|
||||||
|
mVoiceInput = new VoiceInput(context, this);
|
||||||
|
mHints = new Hints(context, new Hints.Display() {
|
||||||
|
public void showHint(int viewResource) {
|
||||||
|
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
|
||||||
|
Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
View view = inflater.inflate(viewResource, null);
|
||||||
|
mContext.setCandidatesView(view);
|
||||||
|
mContext.setCandidatesViewShown(true);
|
||||||
|
mIsShowingHint = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private VoiceIMEConnector() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetVoiceStates(boolean isPasswordText) {
|
||||||
|
mAfterVoiceInput = false;
|
||||||
|
mImmediatelyAfterVoiceInput = false;
|
||||||
|
mShowingVoiceSuggestions = false;
|
||||||
|
mVoiceInputHighlighted = false;
|
||||||
|
mPasswordText = isPasswordText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flushVoiceInputLogs(boolean configurationChanged) {
|
||||||
|
if (VOICE_INSTALLED && !configurationChanged) {
|
||||||
|
if (mAfterVoiceInput) {
|
||||||
|
mVoiceInput.flushAllTextModificationCounters();
|
||||||
|
mVoiceInput.logInputEnded();
|
||||||
|
}
|
||||||
|
mVoiceInput.flushLogs();
|
||||||
|
mVoiceInput.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flushAndLogAllTextModificationCounters(int index, CharSequence suggestion,
|
||||||
|
String wordSeparators) {
|
||||||
|
if (mAfterVoiceInput && mShowingVoiceSuggestions) {
|
||||||
|
mVoiceInput.flushAllTextModificationCounters();
|
||||||
|
// send this intent AFTER logging any prior aggregated edits.
|
||||||
|
mVoiceInput.logTextModifiedByChooseSuggestion(suggestion.toString(), index,
|
||||||
|
wordSeparators, mContext.getCurrentInputConnection());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showVoiceWarningDialog(final boolean swipe, IBinder token,
|
||||||
|
final boolean configurationChanging) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
|
||||||
|
builder.setCancelable(true);
|
||||||
|
builder.setIcon(R.drawable.ic_mic_dialog);
|
||||||
|
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int whichButton) {
|
||||||
|
mVoiceInput.logKeyboardWarningDialogOk();
|
||||||
|
reallyStartListening(swipe, configurationChanging);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int whichButton) {
|
||||||
|
mVoiceInput.logKeyboardWarningDialogCancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mLocaleSupportedForVoiceInput) {
|
||||||
|
String message = mContext.getString(R.string.voice_warning_may_not_understand)
|
||||||
|
+ "\n\n" + mContext.getString(R.string.voice_warning_how_to_turn_off);
|
||||||
|
builder.setMessage(message);
|
||||||
|
} else {
|
||||||
|
String message = mContext.getString(R.string.voice_warning_locale_not_supported)
|
||||||
|
+ "\n\n" + mContext.getString(R.string.voice_warning_may_not_understand)
|
||||||
|
+ "\n\n" + mContext.getString(R.string.voice_warning_how_to_turn_off);
|
||||||
|
builder.setMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setTitle(R.string.voice_warning_title);
|
||||||
|
mVoiceWarningDialog = builder.create();
|
||||||
|
Window window = mVoiceWarningDialog.getWindow();
|
||||||
|
WindowManager.LayoutParams lp = window.getAttributes();
|
||||||
|
lp.token = token;
|
||||||
|
lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
|
||||||
|
window.setAttributes(lp);
|
||||||
|
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
|
||||||
|
mVoiceInput.logKeyboardWarningDialogShown();
|
||||||
|
mVoiceWarningDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showPunctuationHintIfNecessary() {
|
||||||
|
InputConnection ic = mContext.getCurrentInputConnection();
|
||||||
|
if (!mImmediatelyAfterVoiceInput && mAfterVoiceInput && ic != null) {
|
||||||
|
if (mHints.showPunctuationHintIfNecessary(ic)) {
|
||||||
|
mVoiceInput.logPunctuationHintDisplayed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mImmediatelyAfterVoiceInput = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hideVoiceWindow(boolean configurationChanging) {
|
||||||
|
if (!configurationChanging) {
|
||||||
|
if (mAfterVoiceInput)
|
||||||
|
mVoiceInput.logInputEnded();
|
||||||
|
if (mVoiceWarningDialog != null && mVoiceWarningDialog.isShowing()) {
|
||||||
|
mVoiceInput.logKeyboardWarningDialogDismissed();
|
||||||
|
mVoiceWarningDialog.dismiss();
|
||||||
|
mVoiceWarningDialog = null;
|
||||||
|
}
|
||||||
|
if (VOICE_INSTALLED & mRecognizing) {
|
||||||
|
mVoiceInput.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mWordToSuggestions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCursorAndSelection(int newSelEnd, int newSelStart) {
|
||||||
|
if (mAfterVoiceInput) {
|
||||||
|
mVoiceInput.setCursorPos(newSelEnd);
|
||||||
|
mVoiceInput.setSelectionSpan(newSelEnd - newSelStart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVoiceInputHighlighted(boolean b) {
|
||||||
|
mVoiceInputHighlighted = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShowingVoiceSuggestions(boolean b) {
|
||||||
|
mShowingVoiceSuggestions = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVoiceButtonEnabled() {
|
||||||
|
return mVoiceButtonEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVoiceButtonOnPrimary() {
|
||||||
|
return mVoiceButtonOnPrimary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVoiceInputHighlighted() {
|
||||||
|
return mVoiceInputHighlighted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRecognizing() {
|
||||||
|
return mRecognizing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getAndResetIsShowingHint() {
|
||||||
|
boolean ret = mIsShowingHint;
|
||||||
|
mIsShowingHint = false;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void revertVoiceInput() {
|
||||||
|
InputConnection ic = mContext.getCurrentInputConnection();
|
||||||
|
if (ic != null) ic.commitText("", 1);
|
||||||
|
mContext.updateSuggestions();
|
||||||
|
mVoiceInputHighlighted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void commitVoiceInput() {
|
||||||
|
if (VOICE_INSTALLED && mVoiceInputHighlighted) {
|
||||||
|
InputConnection ic = mContext.getCurrentInputConnection();
|
||||||
|
if (ic != null) ic.finishComposingText();
|
||||||
|
mContext.updateSuggestions();
|
||||||
|
mVoiceInputHighlighted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean logAndRevertVoiceInput() {
|
||||||
|
if (VOICE_INSTALLED && mVoiceInputHighlighted) {
|
||||||
|
mVoiceInput.incrementTextModificationDeleteCount(
|
||||||
|
mVoiceResults.candidates.get(0).toString().length());
|
||||||
|
revertVoiceInput();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rememberReplacedWord(CharSequence suggestion,String wordSeparators) {
|
||||||
|
if (mShowingVoiceSuggestions) {
|
||||||
|
// Retain the replaced word in the alternatives array.
|
||||||
|
EditingUtil.Range range = new EditingUtil.Range();
|
||||||
|
String wordToBeReplaced = EditingUtil.getWordAtCursor(
|
||||||
|
mContext.getCurrentInputConnection(), wordSeparators, range);
|
||||||
|
if (!mWordToSuggestions.containsKey(wordToBeReplaced)) {
|
||||||
|
wordToBeReplaced = wordToBeReplaced.toLowerCase();
|
||||||
|
}
|
||||||
|
if (mWordToSuggestions.containsKey(wordToBeReplaced)) {
|
||||||
|
List<CharSequence> suggestions = mWordToSuggestions.get(wordToBeReplaced);
|
||||||
|
if (suggestions.contains(suggestion)) {
|
||||||
|
suggestions.remove(suggestion);
|
||||||
|
}
|
||||||
|
suggestions.add(wordToBeReplaced);
|
||||||
|
mWordToSuggestions.remove(wordToBeReplaced);
|
||||||
|
mWordToSuggestions.put(suggestion.toString(), suggestions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to apply any voice alternatives for the word if this was a spoken word and
|
||||||
|
* there are voice alternatives.
|
||||||
|
* @param touching The word that the cursor is touching, with position information
|
||||||
|
* @return true if an alternative was found, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean applyVoiceAlternatives(EditingUtil.SelectedWord touching) {
|
||||||
|
// Search for result in spoken word alternatives
|
||||||
|
String selectedWord = touching.word.toString().trim();
|
||||||
|
if (!mWordToSuggestions.containsKey(selectedWord)) {
|
||||||
|
selectedWord = selectedWord.toLowerCase();
|
||||||
|
}
|
||||||
|
if (mWordToSuggestions.containsKey(selectedWord)) {
|
||||||
|
mShowingVoiceSuggestions = true;
|
||||||
|
List<CharSequence> suggestions = mWordToSuggestions.get(selectedWord);
|
||||||
|
// If the first letter of touching is capitalized, make all the suggestions
|
||||||
|
// start with a capital letter.
|
||||||
|
if (Character.isUpperCase(touching.word.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mContext.setSuggestions(suggestions, false, true, true);
|
||||||
|
mContext.setCandidatesViewShown(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleBackspace() {
|
||||||
|
if (mAfterVoiceInput) {
|
||||||
|
// Don't log delete if the user is pressing delete at
|
||||||
|
// the beginning of the text box (hence not deleting anything)
|
||||||
|
if (mVoiceInput.getCursorPos() > 0) {
|
||||||
|
// If anything was selected before the delete was pressed, increment the
|
||||||
|
// delete count by the length of the selection
|
||||||
|
int deleteLen = mVoiceInput.getSelectionSpan() > 0 ?
|
||||||
|
mVoiceInput.getSelectionSpan() : 1;
|
||||||
|
mVoiceInput.incrementTextModificationDeleteCount(deleteLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleCharacter() {
|
||||||
|
commitVoiceInput();
|
||||||
|
if (mAfterVoiceInput) {
|
||||||
|
// Assume input length is 1. This assumption fails for smiley face insertions.
|
||||||
|
mVoiceInput.incrementTextModificationInsertCount(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleSeparator() {
|
||||||
|
commitVoiceInput();
|
||||||
|
if (mAfterVoiceInput){
|
||||||
|
// Assume input length is 1. This assumption fails for smiley face insertions.
|
||||||
|
mVoiceInput.incrementTextModificationInsertPunctuationCount(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleClose() {
|
||||||
|
if (VOICE_INSTALLED & mRecognizing) {
|
||||||
|
mVoiceInput.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void handleVoiceResults(KeyboardSwitcher switcher, boolean capitalizeFirstWord) {
|
||||||
|
mAfterVoiceInput = true;
|
||||||
|
mImmediatelyAfterVoiceInput = true;
|
||||||
|
|
||||||
|
InputConnection ic = mContext.getCurrentInputConnection();
|
||||||
|
if (!mContext.isFullscreenMode()) {
|
||||||
|
// Start listening for updates to the text from typing, etc.
|
||||||
|
if (ic != null) {
|
||||||
|
ExtractedTextRequest req = new ExtractedTextRequest();
|
||||||
|
ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mContext.vibrate();
|
||||||
|
mContext.switchToKeyboardView();
|
||||||
|
|
||||||
|
final List<CharSequence> nBest = new ArrayList<CharSequence>();
|
||||||
|
for (String c : mVoiceResults.candidates) {
|
||||||
|
if (capitalizeFirstWord) {
|
||||||
|
c = Character.toUpperCase(c.charAt(0)) + c.substring(1, c.length());
|
||||||
|
}
|
||||||
|
nBest.add(c);
|
||||||
|
}
|
||||||
|
if (nBest.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String bestResult = nBest.get(0).toString();
|
||||||
|
mVoiceInput.logVoiceInputDelivered(bestResult.length());
|
||||||
|
mHints.registerVoiceResult(bestResult);
|
||||||
|
|
||||||
|
if (ic != null) ic.beginBatchEdit(); // To avoid extra updates on committing older text
|
||||||
|
mContext.commitTyped(ic);
|
||||||
|
EditingUtil.appendText(ic, bestResult);
|
||||||
|
if (ic != null) ic.endBatchEdit();
|
||||||
|
|
||||||
|
mVoiceInputHighlighted = true;
|
||||||
|
mWordToSuggestions.putAll(mVoiceResults.alternatives);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchToRecognitionStatusView(final boolean configurationChanging) {
|
||||||
|
final boolean configChanged = configurationChanging;
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
mContext.setCandidatesViewShown(false);
|
||||||
|
mRecognizing = true;
|
||||||
|
View v = mVoiceInput.getView();
|
||||||
|
ViewParent p = v.getParent();
|
||||||
|
if (p != null && p instanceof ViewGroup) {
|
||||||
|
((ViewGroup)v.getParent()).removeView(v);
|
||||||
|
}
|
||||||
|
mContext.setInputView(v);
|
||||||
|
mContext.updateInputViewShown();
|
||||||
|
if (configChanged) {
|
||||||
|
mVoiceInput.onConfigurationChanged();
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reallyStartListening(boolean swipe, final boolean configurationChanging) {
|
||||||
|
if (!mHasUsedVoiceInput) {
|
||||||
|
// The user has started a voice input, so remember that in the
|
||||||
|
// future (so we don't show the warning dialog after the first run).
|
||||||
|
SharedPreferences.Editor editor =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(mContext).edit();
|
||||||
|
editor.putBoolean(PREF_HAS_USED_VOICE_INPUT, true);
|
||||||
|
SharedPreferencesCompat.apply(editor);
|
||||||
|
mHasUsedVoiceInput = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mLocaleSupportedForVoiceInput && !mHasUsedVoiceInputUnsupportedLocale) {
|
||||||
|
// The user has started a voice input from an unsupported locale, so remember that
|
||||||
|
// in the future (so we don't show the warning dialog the next time they do this).
|
||||||
|
SharedPreferences.Editor editor =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(mContext).edit();
|
||||||
|
editor.putBoolean(PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE, true);
|
||||||
|
SharedPreferencesCompat.apply(editor);
|
||||||
|
mHasUsedVoiceInputUnsupportedLocale = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear N-best suggestions
|
||||||
|
mContext.clearSuggestions();
|
||||||
|
|
||||||
|
FieldContext context = makeFieldContext();
|
||||||
|
mVoiceInput.startListening(context, swipe);
|
||||||
|
switchToRecognitionStatusView(configurationChanging);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startListening(final boolean swipe, IBinder token,
|
||||||
|
final boolean configurationChanging) {
|
||||||
|
if (VOICE_INSTALLED) {
|
||||||
|
if (!mHasUsedVoiceInput ||
|
||||||
|
(!mLocaleSupportedForVoiceInput && !mHasUsedVoiceInputUnsupportedLocale)) {
|
||||||
|
// Calls reallyStartListening if user clicks OK, does nothing if user clicks Cancel.
|
||||||
|
showVoiceWarningDialog(swipe, token, configurationChanging);
|
||||||
|
} else {
|
||||||
|
reallyStartListening(swipe, configurationChanging);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean fieldCanDoVoice(FieldContext fieldContext) {
|
||||||
|
return !mPasswordText
|
||||||
|
&& mVoiceInput != null
|
||||||
|
&& !mVoiceInput.isBlacklistedField(fieldContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldShowVoiceButton(FieldContext fieldContext, EditorInfo attribute) {
|
||||||
|
return ENABLE_VOICE_BUTTON && fieldCanDoVoice(fieldContext)
|
||||||
|
&& !(attribute != null
|
||||||
|
&& IME_OPTION_NO_MICROPHONE.equals(attribute.privateImeOptions))
|
||||||
|
&& SpeechRecognizer.isRecognitionAvailable(mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadSettings(EditorInfo attribute, SharedPreferences sp) {
|
||||||
|
mHasUsedVoiceInput = sp.getBoolean(PREF_HAS_USED_VOICE_INPUT, false);
|
||||||
|
mHasUsedVoiceInputUnsupportedLocale =
|
||||||
|
sp.getBoolean(PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE, false);
|
||||||
|
|
||||||
|
mLocaleSupportedForVoiceInput = SubtypeSwitcher.getInstance().isVoiceSupported(
|
||||||
|
SubtypeSwitcher.getInstance().getInputLocaleStr());
|
||||||
|
|
||||||
|
if (VOICE_INSTALLED) {
|
||||||
|
final String voiceMode = sp.getString(PREF_VOICE_MODE,
|
||||||
|
mContext.getString(R.string.voice_mode_main));
|
||||||
|
mVoiceButtonEnabled = !voiceMode.equals(mContext.getString(R.string.voice_mode_off))
|
||||||
|
&& shouldShowVoiceButton(makeFieldContext(), attribute);
|
||||||
|
mVoiceButtonOnPrimary = voiceMode.equals(mContext.getString(R.string.voice_mode_main));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
if (VOICE_INSTALLED && mVoiceInput != null) {
|
||||||
|
mVoiceInput.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onConfigurationChanged(boolean configurationChanging) {
|
||||||
|
if (mRecognizing) {
|
||||||
|
switchToRecognitionStatusView(configurationChanging);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onCancelVoice() {
|
||||||
|
if (mRecognizing) {
|
||||||
|
mRecognizing = false;
|
||||||
|
mContext.switchToKeyboardView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onVoiceResults(List<String> candidates,
|
||||||
|
Map<String, List<CharSequence>> alternatives) {
|
||||||
|
if (!mRecognizing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mVoiceResults.candidates = candidates;
|
||||||
|
mVoiceResults.alternatives = alternatives;
|
||||||
|
mHandler.updateVoiceResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldContext makeFieldContext() {
|
||||||
|
SubtypeSwitcher switcher = SubtypeSwitcher.getInstance();
|
||||||
|
return new FieldContext(mContext.getCurrentInputConnection(),
|
||||||
|
mContext.getCurrentInputEditorInfo(), switcher.getInputLocaleStr(),
|
||||||
|
switcher.getEnabledLanguages());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VoiceResults {
|
||||||
|
List<String> candidates;
|
||||||
|
Map<String, List<CharSequence>> alternatives;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue