aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java153
-rw-r--r--java/src/com/android/inputmethod/latin/Settings.java6
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java8
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java2
-rw-r--r--java/src/com/android/inputmethod/voice/RecognitionView.java221
-rw-r--r--java/src/com/android/inputmethod/voice/SoundIndicator.java155
-rw-r--r--java/src/com/android/inputmethod/voice/VoiceIMEConnector.java54
-rw-r--r--java/src/com/android/inputmethod/voice/VoiceInput.java13
8 files changed, 412 insertions, 200 deletions
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index cd4143e78..5d48d6b36 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -133,14 +133,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private Resources mResources;
private SharedPreferences mPrefs;
+ // These variables are initialized according to the {@link EditorInfo#inputType}.
+ private boolean mAutoSpace;
+ private boolean mInputTypeNoAutoCorrect;
+ private boolean mIsSettingsSuggestionStripOn;
+ private boolean mApplicationSpecifiedCompletionOn;
+
private final StringBuilder mComposing = new StringBuilder();
private WordComposer mWord = new WordComposer();
private CharSequence mBestWord;
private boolean mHasValidSuggestions;
- private boolean mIsSettingsSuggestionStripOn;
- private boolean mApplicationSpecifiedCompletionOn;
private boolean mHasDictionary;
- private boolean mAutoSpace;
private boolean mJustAddedAutoSpace;
private boolean mAutoCorrectEnabled;
private boolean mReCorrectionEnabled;
@@ -164,9 +167,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private int mLastSelectionEnd;
private SuggestedWords mSuggestPuncList;
- // 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;
private boolean mJustReverted;
@@ -353,8 +353,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final Resources res = getResources();
mResources = res;
- mReCorrectionEnabled = prefs.getBoolean(Settings.PREF_RECORRECTION_ENABLED,
- res.getBoolean(R.bool.default_recorrection_enabled));
+
+ // If the option should not be shown, do not read the recorrection preference
+ // but always use the default setting defined in the resources.
+ if (res.getBoolean(R.bool.config_enable_show_recorrection_option)) {
+ mReCorrectionEnabled = prefs.getBoolean(Settings.PREF_RECORRECTION_ENABLED,
+ res.getBoolean(R.bool.default_recorrection_enabled));
+ } else {
+ mReCorrectionEnabled = res.getBoolean(R.bool.default_recorrection_enabled);
+ }
+
mConfigSwipeDownDismissKeyboardEnabled = res.getBoolean(
R.bool.config_swipe_down_dismiss_keyboard_enabled);
mConfigDelayBeforeFadeoutLanguageOnSpacebar = res.getInteger(
@@ -456,7 +464,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mConfigurationChanging = true;
super.onConfigurationChanged(conf);
- mVoiceConnector.onConfigurationChanged(mConfigurationChanging);
+ mVoiceConnector.onConfigurationChanged(conf);
mConfigurationChanging = false;
}
@@ -507,24 +515,62 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mRefreshKeyboardRequired) {
mRefreshKeyboardRequired = false;
- onKeyboardLanguageChanged();
+ onRefreshKeyboard();
}
TextEntryState.newSession(this);
- // 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
- // the switch statement) whether we want to enable the voice button.
- int variation = attribute.inputType & InputType.TYPE_MASK_VARIATION;
- mVoiceConnector.resetVoiceStates(isPasswordVariation(variation));
+ // Most such things we decide below in initializeInputAttributesAndGetMode, but we need to
+ // know now whether this is a password text field, because we need to know now whether we
+ // want to enable the voice button.
+ mVoiceConnector.resetVoiceStates(isPasswordVariation(
+ attribute.inputType & InputType.TYPE_MASK_VARIATION));
+
+ final int mode = initializeInputAttributesAndGetMode(attribute.inputType);
+
+ inputView.closing();
+ mEnteredText = null;
+ mComposing.setLength(0);
+ mHasValidSuggestions = false;
+ mDeleteCount = 0;
+ mJustAddedAutoSpace = false;
+
+ loadSettings(attribute);
+ if (mSubtypeSwitcher.isKeyboardMode()) {
+ switcher.loadKeyboard(mode, attribute.imeOptions,
+ mVoiceConnector.isVoiceButtonEnabled(),
+ mVoiceConnector.isVoiceButtonOnPrimary());
+ switcher.updateShiftState();
+ }
+
+ setCandidatesViewShownInternal(isCandidateStripVisible(),
+ false /* needsInputViewShown */ );
+ // Delay updating suggestions because keyboard input view may not be shown at this point.
+ mHandler.postUpdateSuggestions();
+
+ updateCorrectionMode();
+
+ inputView.setPreviewEnabled(mPopupOn);
+ inputView.setProximityCorrectionEnabled(true);
+ // If we just entered a text field, maybe it has some old text that requires correction
+ checkReCorrectionOnStart();
+ inputView.setForeground(true);
+
+ mVoiceConnector.onStartInputView(inputView.getWindowToken());
+
+ if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
+ }
+
+ private int initializeInputAttributesAndGetMode(int inputType) {
+ final int variation = inputType & InputType.TYPE_MASK_VARIATION;
+ mAutoSpace = false;
mInputTypeNoAutoCorrect = false;
mIsSettingsSuggestionStripOn = false;
mApplicationSpecifiedCompletionOn = false;
mApplicationSpecifiedCompletions = null;
- mEnteredText = null;
final int mode;
- switch (attribute.inputType & InputType.TYPE_MASK_CLASS) {
+ switch (inputType & InputType.TYPE_MASK_CLASS) {
case InputType.TYPE_CLASS_NUMBER:
case InputType.TYPE_CLASS_DATETIME:
mode = KeyboardId.MODE_NUMBER;
@@ -559,7 +605,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mode = KeyboardId.MODE_WEB;
// If it's a browser edit field and auto correct is not ON explicitly, then
// disable auto correction, but keep suggestions on.
- if ((attribute.inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) {
+ if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) {
mInputTypeNoAutoCorrect = true;
}
} else {
@@ -567,16 +613,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
// If NO_SUGGESTIONS is set, don't do prediction.
- if ((attribute.inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) {
+ if ((inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) {
mIsSettingsSuggestionStripOn = false;
mInputTypeNoAutoCorrect = true;
}
// If it's not multiline and the autoCorrect flag is not set, then don't correct
- if ((attribute.inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0 &&
- (attribute.inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0) {
+ if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0 &&
+ (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0) {
mInputTypeNoAutoCorrect = true;
}
- if ((attribute.inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
+ if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
mIsSettingsSuggestionStripOn = false;
mApplicationSpecifiedCompletionOn = isFullscreenMode();
}
@@ -585,40 +631,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mode = KeyboardId.MODE_TEXT;
break;
}
- inputView.closing();
- mComposing.setLength(0);
- mHasValidSuggestions = false;
- mDeleteCount = 0;
- mJustAddedAutoSpace = false;
-
- loadSettings(attribute);
- if (mSubtypeSwitcher.isKeyboardMode()) {
- switcher.loadKeyboard(mode, attribute.imeOptions,
- mVoiceConnector.isVoiceButtonEnabled(),
- mVoiceConnector.isVoiceButtonOnPrimary());
- switcher.updateShiftState();
- }
-
- setCandidatesViewShownInternal(isCandidateStripVisible(),
- false /* needsInputViewShown */ );
- // Delay updating suggestions because keyboard input view may not be shown at this point.
- mHandler.postUpdateSuggestions();
-
- // If the dictionary is not big enough, don't auto correct
- mHasDictionary = mSuggest.hasMainDictionary();
-
- updateCorrectionMode();
-
- inputView.setPreviewEnabled(mPopupOn);
- inputView.setProximityCorrectionEnabled(true);
- mIsSettingsSuggestionStripOn &= (mCorrectionMode > 0 || isShowingSuggestionsStrip());
- // If we just entered a text field, maybe it has some old text that requires correction
- checkReCorrectionOnStart();
- inputView.setForeground(true);
-
- mVoiceConnector.onStartInputView(inputView.getWindowToken());
-
- if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
+ return mode;
}
private void checkReCorrectionOnStart() {
@@ -1387,7 +1400,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private boolean isSuggestionsRequested() {
- return mIsSettingsSuggestionStripOn;
+ return mIsSettingsSuggestionStripOn
+ && (mCorrectionMode > 0 || isShowingSuggestionsStrip());
}
private boolean isShowingPunctuationList() {
@@ -1805,7 +1819,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final int length = mComposing.length();
if (!mHasValidSuggestions && length > 0) {
final InputConnection ic = getCurrentInputConnection();
- mHasValidSuggestions = true;
mJustReverted = true;
final CharSequence punctuation = ic.getTextBeforeCursor(1, 0);
if (deleteChar) ic.deleteSurroundingText(1, 0);
@@ -1815,14 +1828,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
toDelete--;
}
ic.deleteSurroundingText(toDelete, 0);
- if (deleteChar) {
+ // Re-insert punctuation only when the deleted character was word separator and the
+ // composing text wasn't equal to the auto-corrected text.
+ if (deleteChar
+ && !TextUtils.isEmpty(punctuation) && isWordSeparator(punctuation.charAt(0))
+ && !TextUtils.equals(mComposing, toTheLeft)) {
ic.commitText(mComposing, 1);
TextEntryState.acceptedTyped(mComposing);
- if (!TextUtils.isEmpty(punctuation) && isWordSeparator(punctuation.charAt(0))) {
- ic.commitText(punctuation, 1);
- TextEntryState.typedCharacter(punctuation.charAt(0), true);
- }
+ ic.commitText(punctuation, 1);
+ TextEntryState.typedCharacter(punctuation.charAt(0), true);
+ // Clear composing text
+ mComposing.setLength(0);
} else {
+ mHasValidSuggestions = true;
ic.setComposingText(mComposing, 1);
TextEntryState.backspace();
}
@@ -1855,9 +1873,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return mWord.isFirstCharCapitalized();
}
- // Notify that Language has been changed and toggleLanguage will update KeyboaredID according
- // to new Language.
- public void onKeyboardLanguageChanged() {
+ // Notify that language or mode have been changed and toggleLanguage will update KeyboaredID
+ // according to new language or mode.
+ public void onRefreshKeyboard() {
toggleLanguage(true, true);
}
@@ -1868,8 +1886,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
// Reload keyboard because the current language has been changed.
KeyboardSwitcher switcher = mKeyboardSwitcher;
- final int mode = switcher.getKeyboardMode();
final EditorInfo attribute = getCurrentInputEditorInfo();
+ final int mode = initializeInputAttributesAndGetMode((attribute != null)
+ ? attribute.inputType : 0);
final int imeOptions = (attribute != null) ? attribute.imeOptions : 0;
switcher.loadKeyboard(mode, imeOptions, mVoiceConnector.isVoiceButtonEnabled(),
mVoiceConnector.isVoiceButtonOnPrimary());
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 5db63b719..75ebbe7e5 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -140,6 +140,12 @@ public class Settings extends PreferenceActivity
if (!showPopupOption) {
getPreferenceScreen().removePreference(findPreference(PREF_POPUP_ON));
}
+
+ final boolean showRecorrectionOption = getResources().getBoolean(
+ R.bool.config_enable_show_recorrection_option);
+ if (!showRecorrectionOption) {
+ getPreferenceScreen().removePreference(findPreference(PREF_RECORRECTION_ENABLED));
+ }
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index b765fad3f..a9bd114f3 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -187,7 +187,7 @@ public class SubtypeSwitcher {
// fallback to the default locale and mode.
Log.w(TAG, "Couldn't get the current subtype.");
newLocale = "en_US";
- newMode =KEYBOARD_MODE;
+ newMode = KEYBOARD_MODE;
} else {
newLocale = newSubtype.getLocale();
newMode = newSubtype.getMode();
@@ -217,8 +217,8 @@ public class SubtypeSwitcher {
mVoiceInput.cancel();
}
}
- if (languageChanged) {
- mService.onKeyboardLanguageChanged();
+ if (modeChanged || languageChanged) {
+ mService.onRefreshKeyboard();
}
} else if (isVoiceMode()) {
// If needsToShowWarningDialog is true, voice input need to show warning before
@@ -439,7 +439,7 @@ public class SubtypeSwitcher {
private void triggerVoiceIME() {
if (!mService.isInputViewShown()) return;
VoiceIMEConnector.getInstance().startListening(false,
- KeyboardSwitcher.getInstance().getInputView().getWindowToken(), false);
+ KeyboardSwitcher.getInstance().getInputView().getWindowToken());
}
//////////////////////////////////////
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 5398b77b2..0fbbcdd91 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -124,7 +124,7 @@ public class SuggestedWords {
addWord(previousSuggestions.getWord(pos));
mIsCompletions = false;
mTypedWordValid = false;
- mHasMinimalSuggestion = (previousSize > 1);
+ mHasMinimalSuggestion = false;
return this;
}
diff --git a/java/src/com/android/inputmethod/voice/RecognitionView.java b/java/src/com/android/inputmethod/voice/RecognitionView.java
index d6d0721e2..98db9365f 100644
--- a/java/src/com/android/inputmethod/voice/RecognitionView.java
+++ b/java/src/com/android/inputmethod/voice/RecognitionView.java
@@ -16,9 +16,6 @@
package com.android.inputmethod.voice;
-import com.android.inputmethod.latin.R;
-
-import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -29,20 +26,21 @@ import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.util.TypedValue;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.ViewGroup.MarginLayoutParams;
+import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
+import com.android.inputmethod.latin.R;
+
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -58,79 +56,55 @@ public class RecognitionView {
private View mView;
private Context mContext;
- private ImageView mImage;
private TextView mText;
- private View mButton;
- private TextView mButtonText;
+ private ImageView mImage;
private View mProgress;
+ private SoundIndicator mSoundIndicator;
+ private Button mButton;
private Drawable mInitializing;
private Drawable mError;
- private List<Drawable> mSpeakNow;
- private float mVolume = 0.0f;
- private int mLevel = 0;
-
- private enum State {LISTENING, WORKING, READY}
- private State mState = State.READY;
-
- private float mMinMicrophoneLevel;
- private float mMaxMicrophoneLevel;
-
- /** Updates the microphone icon to show user their volume.*/
- private Runnable mUpdateVolumeRunnable = new Runnable() {
- @Override
- public void run() {
- if (mState != State.LISTENING) {
- return;
- }
-
- final float min = mMinMicrophoneLevel;
- final float max = mMaxMicrophoneLevel;
- final int maxLevel = mSpeakNow.size() - 1;
+ private static final int INIT = 0;
+ private static final int LISTENING = 1;
+ private static final int WORKING = 2;
+ private static final int READY = 3;
+
+ private int mState = INIT;
- int index = (int) ((mVolume - min) / (max - min) * maxLevel);
- final int level = Math.min(Math.max(0, index), maxLevel);
+ private final View mPopupLayout;
- if (level != mLevel) {
- mImage.setImageDrawable(mSpeakNow.get(level));
- mLevel = level;
- }
- mUiHandler.postDelayed(mUpdateVolumeRunnable, 50);
- }
- };
+ private final Drawable mListeningBorder;
+ private final Drawable mWorkingBorder;
+ private final Drawable mErrorBorder;
public RecognitionView(Context context, OnClickListener clickListener) {
mUiHandler = new Handler();
- mView = LayoutInflater.from(context).inflate(R.layout.recognition_status, null);
- ContentResolver cr = context.getContentResolver();
- mMinMicrophoneLevel = SettingsUtil.getSettingsFloat(
- cr, SettingsUtil.LATIN_IME_MIN_MICROPHONE_LEVEL, 15.f);
- mMaxMicrophoneLevel = SettingsUtil.getSettingsFloat(
- cr, SettingsUtil.LATIN_IME_MAX_MICROPHONE_LEVEL, 30.f);
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+ mView = inflater.inflate(R.layout.recognition_status, null);
+
+ mPopupLayout= mView.findViewById(R.id.popup_layout);
// Pre-load volume level images
Resources r = context.getResources();
- mSpeakNow = new ArrayList<Drawable>();
- mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level0));
- mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level1));
- mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level2));
- mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level3));
- mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level4));
- mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level5));
- mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level6));
+ mListeningBorder = r.getDrawable(R.drawable.vs_dialog_red);
+ mWorkingBorder = r.getDrawable(R.drawable.vs_dialog_blue);
+ mErrorBorder = r.getDrawable(R.drawable.vs_dialog_yellow);
mInitializing = r.getDrawable(R.drawable.mic_slash);
mError = r.getDrawable(R.drawable.caution);
mImage = (ImageView) mView.findViewById(R.id.image);
- mButton = mView.findViewById(R.id.button);
+ mProgress = mView.findViewById(R.id.progress);
+ mSoundIndicator = (SoundIndicator) mView.findViewById(R.id.sound_indicator);
+
+ mButton = (Button) mView.findViewById(R.id.button);
mButton.setOnClickListener(clickListener);
mText = (TextView) mView.findViewById(R.id.text);
- mButtonText = (TextView) mView.findViewById(R.id.button_text);
- mProgress = mView.findViewById(R.id.progress);
mContext = context;
}
@@ -144,9 +118,9 @@ public class RecognitionView {
@Override
public void run() {
// Restart the spinner
- if (mState == State.WORKING) {
- ((ProgressBar)mProgress).setIndeterminate(false);
- ((ProgressBar)mProgress).setIndeterminate(true);
+ if (mState == WORKING) {
+ ((ProgressBar) mProgress).setIndeterminate(false);
+ ((ProgressBar) mProgress).setIndeterminate(true);
}
}
});
@@ -156,48 +130,48 @@ public class RecognitionView {
mUiHandler.post(new Runnable() {
@Override
public void run() {
- prepareDialog(false, mContext.getText(R.string.voice_initializing), mInitializing,
- mContext.getText(R.string.cancel));
+ mState = INIT;
+ prepareDialog(mContext.getText(R.string.voice_initializing), mInitializing,
+ mContext.getText(R.string.cancel));
}
});
}
public void showListening() {
+ Log.d(TAG, "#showListening");
mUiHandler.post(new Runnable() {
@Override
public void run() {
- mState = State.LISTENING;
- prepareDialog(false, mContext.getText(R.string.voice_listening), mSpeakNow.get(0),
+ mState = LISTENING;
+ prepareDialog(mContext.getText(R.string.voice_listening), null,
mContext.getText(R.string.cancel));
}
});
- mUiHandler.postDelayed(mUpdateVolumeRunnable, 50);
}
- public void updateVoiceMeter(final float rmsdB) {
- mVolume = rmsdB;
+ public void updateVoiceMeter(float rmsdB) {
+ mSoundIndicator.setRmsdB(rmsdB);
}
public void showError(final String message) {
mUiHandler.post(new Runnable() {
@Override
public void run() {
- mState = State.READY;
- prepareDialog(false, message, mError, mContext.getText(R.string.ok));
+ mState = READY;
+ prepareDialog(message, mError, mContext.getText(R.string.ok));
}
- });
+ });
}
public void showWorking(
final ByteArrayOutputStream waveBuffer,
final int speechStartPosition,
final int speechEndPosition) {
-
mUiHandler.post(new Runnable() {
@Override
public void run() {
- mState = State.WORKING;
- prepareDialog(true, mContext.getText(R.string.voice_working), null, mContext
+ mState = WORKING;
+ prepareDialog(mContext.getText(R.string.voice_working), null, mContext
.getText(R.string.cancel));
final ShortBuffer buf = ByteBuffer.wrap(waveBuffer.toByteArray()).order(
ByteOrder.nativeOrder()).asShortBuffer();
@@ -205,21 +179,71 @@ public class RecognitionView {
waveBuffer.reset();
showWave(buf, speechStartPosition / 2, speechEndPosition / 2);
}
- });
+ });
}
- private void prepareDialog(boolean spinVisible, CharSequence text, Drawable image,
+ private void prepareDialog(CharSequence text, Drawable image,
CharSequence btnTxt) {
- if (spinVisible) {
- mProgress.setVisibility(View.VISIBLE);
- mImage.setVisibility(View.GONE);
- } else {
- mProgress.setVisibility(View.GONE);
- mImage.setImageDrawable(image);
- mImage.setVisibility(View.VISIBLE);
+ switch (mState) {
+ case INIT:
+ mText.setVisibility(View.GONE);
+
+ mProgress.setVisibility(View.GONE);
+
+ mImage.setVisibility(View.VISIBLE);
+ mImage.setImageResource(R.drawable.mic_slash);
+
+ mSoundIndicator.setVisibility(View.GONE);
+ mSoundIndicator.stop();
+
+ mPopupLayout.setBackgroundDrawable(mListeningBorder);
+ break;
+ case LISTENING:
+ mText.setVisibility(View.VISIBLE);
+ mText.setText(text);
+
+ mProgress.setVisibility(View.GONE);
+
+ mImage.setVisibility(View.GONE);
+
+ mSoundIndicator.setVisibility(View.VISIBLE);
+ mSoundIndicator.start();
+
+ mPopupLayout.setBackgroundDrawable(mListeningBorder);
+ break;
+ case WORKING:
+
+ mText.setVisibility(View.VISIBLE);
+ mText.setText(text);
+
+ mProgress.setVisibility(View.VISIBLE);
+
+ mImage.setVisibility(View.VISIBLE);
+
+ mSoundIndicator.setVisibility(View.GONE);
+ mSoundIndicator.stop();
+
+ mPopupLayout.setBackgroundDrawable(mWorkingBorder);
+ break;
+ case READY:
+ mText.setVisibility(View.VISIBLE);
+ mText.setText(text);
+
+ mProgress.setVisibility(View.GONE);
+
+ mImage.setVisibility(View.VISIBLE);
+ mImage.setImageResource(R.drawable.caution);
+
+ mSoundIndicator.setVisibility(View.GONE);
+ mSoundIndicator.stop();
+
+ mPopupLayout.setBackgroundDrawable(mErrorBorder);
+ break;
+ default:
+ Log.w(TAG, "Unknown state " + mState);
}
- mText.setText(text);
- mButtonText.setText(btnTxt);
+ mPopupLayout.requestLayout();
+ mButton.setText(btnTxt);
}
/**
@@ -246,7 +270,7 @@ public class RecognitionView {
*/
private void showWave(ShortBuffer waveBuffer, int startPosition, int endPosition) {
final int w = ((View) mImage.getParent()).getWidth();
- final int h = mImage.getHeight();
+ final int h = ((View) mImage.getParent()).getHeight();
if (w <= 0 || h <= 0) {
// view is not visible this time. Skip drawing.
return;
@@ -257,7 +281,7 @@ public class RecognitionView {
paint.setColor(0xFFFFFFFF); // 0xAARRGGBB
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
- paint.setAlpha(0x90);
+ paint.setAlpha(80);
final PathEffect effect = new CornerPathEffect(3);
paint.setPathEffect(effect);
@@ -279,7 +303,7 @@ public class RecognitionView {
final int count = (endIndex - startIndex) / numSamplePerWave;
final float deltaX = 1.0f * w / count;
- int yMax = h / 2 - 8;
+ int yMax = h / 2;
Path path = new Path();
c.translate(0, yMax);
float x = 0;
@@ -293,37 +317,20 @@ public class RecognitionView {
path.lineTo(x, y);
}
if (deltaX > 4) {
- paint.setStrokeWidth(3);
+ paint.setStrokeWidth(2);
} else {
- paint.setStrokeWidth(Math.max(1, (int) (deltaX -.05)));
+ paint.setStrokeWidth(Math.max(0, (int) (deltaX -.05)));
}
c.drawPath(path, paint);
mImage.setImageBitmap(b);
- mImage.setVisibility(View.VISIBLE);
- MarginLayoutParams mProgressParams = (MarginLayoutParams)mProgress.getLayoutParams();
- mProgressParams.topMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
- -h , mContext.getResources().getDisplayMetrics());
-
- // Tweak the padding manually to fill out the whole view horizontally.
- // TODO: Do this in the xml layout instead.
- ((View) mImage.getParent()).setPadding(4, ((View) mImage.getParent()).getPaddingTop(), 3,
- ((View) mImage.getParent()).getPaddingBottom());
- mProgress.setLayoutParams(mProgressParams);
}
-
public void finish() {
mUiHandler.post(new Runnable() {
@Override
public void run() {
- mState = State.READY;
- exitWorking();
+ mSoundIndicator.stop();
}
- });
- }
-
- private void exitWorking() {
- mProgress.setVisibility(View.GONE);
- mImage.setVisibility(View.VISIBLE);
+ });
}
}
diff --git a/java/src/com/android/inputmethod/voice/SoundIndicator.java b/java/src/com/android/inputmethod/voice/SoundIndicator.java
new file mode 100644
index 000000000..543290b32
--- /dev/null
+++ b/java/src/com/android/inputmethod/voice/SoundIndicator.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2011 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.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import com.android.inputmethod.latin.R;
+
+/**
+ * A widget which shows the volume of audio using a microphone icon
+ */
+public class SoundIndicator extends ImageView {
+ @SuppressWarnings("unused")
+ private static final String TAG = "SoundIndicator";
+
+ private static final float UP_SMOOTHING_FACTOR = 0.9f;
+ private static final float DOWN_SMOOTHING_FACTOR = 0.4f;
+
+ private static final float AUDIO_METER_MIN_DB = 7.0f;
+ private static final float AUDIO_METER_DB_RANGE = 20.0f;
+
+ private static final long FRAME_DELAY = 50;
+
+ private Bitmap mDrawingBuffer;
+ private Canvas mBufferCanvas;
+ private Bitmap mEdgeBitmap;
+ private float mLevel = 0.0f;
+ private Drawable mFrontDrawable;
+ private Paint mClearPaint;
+ private Paint mMultPaint;
+ private int mEdgeBitmapOffset;
+
+ private Handler mHandler;
+
+ private Runnable mDrawFrame = new Runnable() {
+ public void run() {
+ invalidate();
+ mHandler.postDelayed(mDrawFrame, FRAME_DELAY);
+ }
+ };
+
+ public SoundIndicator(Context context) {
+ this(context, null);
+ }
+
+ public SoundIndicator(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mFrontDrawable = getDrawable();
+ BitmapDrawable edgeDrawable =
+ (BitmapDrawable) context.getResources().getDrawable(R.drawable.vs_popup_mic_edge);
+ mEdgeBitmap = edgeDrawable.getBitmap();
+ mEdgeBitmapOffset = mEdgeBitmap.getHeight() / 2;
+
+ mDrawingBuffer =
+ Bitmap.createBitmap(mFrontDrawable.getIntrinsicWidth(),
+ mFrontDrawable.getIntrinsicHeight(), Config.ARGB_8888);
+
+ mBufferCanvas = new Canvas(mDrawingBuffer);
+
+ // Initialize Paints.
+ mClearPaint = new Paint();
+ mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+
+ mMultPaint = new Paint();
+ mMultPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
+
+ mHandler = new Handler();
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ //super.onDraw(canvas);
+
+ float w = getWidth();
+ float h = getHeight();
+
+ // Clear the buffer canvas
+ mBufferCanvas.drawRect(0, 0, w, h, mClearPaint);
+
+ // Set its clip so we don't draw the front image all the way to the top
+ Rect clip = new Rect(0,
+ (int) ((1.0 - mLevel) * (h + mEdgeBitmapOffset)) - mEdgeBitmapOffset,
+ (int) w,
+ (int) h);
+
+ mBufferCanvas.save();
+ mBufferCanvas.clipRect(clip);
+
+ // Draw the front image
+ mFrontDrawable.setBounds(new Rect(0, 0, (int) w, (int) h));
+ mFrontDrawable.draw(mBufferCanvas);
+
+ mBufferCanvas.restore();
+
+ // Draw the edge image on top of the buffer image with a multiply mode
+ mBufferCanvas.drawBitmap(mEdgeBitmap, 0, clip.top, mMultPaint);
+
+ // Draw the buffer image (on top of the background image)
+ canvas.drawBitmap(mDrawingBuffer, 0, 0, null);
+ }
+
+ /**
+ * Sets the sound level
+ *
+ * @param rmsdB The level of the sound, in dB.
+ */
+ public void setRmsdB(float rmsdB) {
+ float level = ((rmsdB - AUDIO_METER_MIN_DB) / AUDIO_METER_DB_RANGE);
+
+ level = Math.min(Math.max(0.0f, level), 1.0f);
+
+ // We smooth towards the new level
+ if (level > mLevel) {
+ mLevel = (level - mLevel) * UP_SMOOTHING_FACTOR + mLevel;
+ } else {
+ mLevel = (level - mLevel) * DOWN_SMOOTHING_FACTOR + mLevel;
+ }
+ invalidate();
+ }
+
+ public void start() {
+ mHandler.post(mDrawFrame);
+ }
+
+ public void stop() {
+ mHandler.removeCallbacks(mDrawFrame);
+ }
+}
diff --git a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
index 9a6c3a83a..a02dbcb55 100644
--- a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
+++ b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
@@ -30,6 +30,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.net.Uri;
import android.os.IBinder;
import android.preference.PreferenceManager;
@@ -78,6 +79,9 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
// dialog is already showing a voice search button.
private static final String IME_OPTION_NO_MICROPHONE = "nm";
+ @SuppressWarnings("unused")
+ private static final String TAG = "VoiceIMEConnector";
+
private boolean mAfterVoiceInput;
private boolean mHasUsedVoiceInput;
private boolean mHasUsedVoiceInputUnsupportedLocale;
@@ -164,8 +168,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
}
}
- private void showVoiceWarningDialog(final boolean swipe, IBinder token,
- final boolean configurationChanging) {
+ private void showVoiceWarningDialog(final boolean swipe, IBinder token) {
if (mVoiceWarningDialog != null && mVoiceWarningDialog.isShowing()) {
return;
}
@@ -176,7 +179,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
mVoiceInput.logKeyboardWarningDialogOk();
- reallyStartListening(swipe, configurationChanging);
+ reallyStartListening(swipe);
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@@ -519,22 +522,38 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
onCancelVoice();
}
- public void switchToRecognitionStatusView(final boolean configurationChanging) {
- final boolean configChanged = configurationChanging;
+ public void switchToRecognitionStatusView(final Configuration configuration) {
mHandler.post(new Runnable() {
@Override
public void run() {
mService.setCandidatesViewShown(false);
mRecognizing = true;
+ mVoiceInput.newView();
View v = mVoiceInput.getView();
+
ViewParent p = v.getParent();
if (p != null && p instanceof ViewGroup) {
- ((ViewGroup)p).removeView(v);
+ ((ViewGroup) p).removeView(v);
+ }
+
+ View keyboardView = KeyboardSwitcher.getInstance().getInputView();
+
+ // The full height of the keyboard is difficult to calculate
+ // as the dimension is expressed in "mm" and not in "pixel"
+ // As we add mm, we don't know how the rounding is going to work
+ // thus we may end up with few pixels extra (or less).
+ if (keyboardView != null) {
+ int h = keyboardView.getHeight();
+ if (h > 0) {
+ View popupLayout = v.findViewById(R.id.popup_layout);
+ popupLayout.getLayoutParams().height = h;
+ }
}
mService.setInputView(v);
mService.updateInputViewShown();
- if (configChanged) {
- mVoiceInput.onConfigurationChanged();
+
+ if (configuration != null) {
+ mVoiceInput.onConfigurationChanged(configuration);
}
}});
}
@@ -544,7 +563,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
mImm.switchToLastInputMethod(token);
}
- private void reallyStartListening(boolean swipe, final boolean configurationChanging) {
+ private void reallyStartListening(boolean swipe) {
if (!VOICE_INSTALLED) {
return;
}
@@ -573,22 +592,21 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
FieldContext context = makeFieldContext();
mVoiceInput.startListening(context, swipe);
- switchToRecognitionStatusView(configurationChanging);
+ switchToRecognitionStatusView(null);
}
- public void startListening(final boolean swipe, IBinder token,
- final boolean configurationChanging) {
+ public void startListening(final boolean swipe, IBinder token) {
+ // TODO: remove swipe which is no longer used.
if (VOICE_INSTALLED) {
if (needsToShowWarningDialog()) {
// Calls reallyStartListening if user clicks OK, does nothing if user clicks Cancel.
- showVoiceWarningDialog(swipe, token, configurationChanging);
+ showVoiceWarningDialog(swipe, token);
} else {
- reallyStartListening(swipe, configurationChanging);
+ reallyStartListening(swipe);
}
}
}
-
private boolean fieldCanDoVoice(FieldContext fieldContext) {
return !mPasswordText
&& mVoiceInput != null
@@ -632,7 +650,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
// Close keyboard view if it is been shown.
if (KeyboardSwitcher.getInstance().isInputViewShown())
KeyboardSwitcher.getInstance().getInputView().purgeKeyboardAndClosing();
- startListening(false, token, false);
+ startListening(false, token);
}
// If we have no token, onAttachedToWindow will take care of showing dialog and start
// listening.
@@ -644,9 +662,9 @@ public class VoiceIMEConnector implements VoiceInput.UiListener {
mSubtypeSwitcher.setVoiceInput(mVoiceInput);
}
- public void onConfigurationChanged(boolean configurationChanging) {
+ public void onConfigurationChanged(Configuration configuration) {
if (mRecognizing) {
- switchToRecognitionStatusView(configurationChanging);
+ switchToRecognitionStatusView(configuration);
}
}
diff --git a/java/src/com/android/inputmethod/voice/VoiceInput.java b/java/src/com/android/inputmethod/voice/VoiceInput.java
index f77b4dd0d..ffa349fde 100644
--- a/java/src/com/android/inputmethod/voice/VoiceInput.java
+++ b/java/src/com/android/inputmethod/voice/VoiceInput.java
@@ -22,6 +22,7 @@ import com.android.inputmethod.latin.R;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -129,12 +130,17 @@ public class VoiceInput implements OnClickListener {
private final static int MSG_CLOSE_ERROR_DIALOG = 1;
+ private final static int MSG_RESET = 2;
+
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- if (msg.what == MSG_CLOSE_ERROR_DIALOG) {
+ if (msg.what == MSG_RESET || msg.what == MSG_CLOSE_ERROR_DIALOG) {
mState = DEFAULT;
mRecognitionView.finish();
+ }
+
+ if (msg.what == MSG_CLOSE_ERROR_DIALOG) {
mUiListener.onCancelVoice();
}
}
@@ -277,8 +283,9 @@ public class VoiceInput implements OnClickListener {
* The configuration of the IME changed and may have caused the views to be layed out
* again. Restore the state of the recognition view.
*/
- public void onConfigurationChanged() {
+ public void onConfigurationChanged(Configuration configuration) {
mRecognitionView.restoreState();
+ mRecognitionView.getView().dispatchConfigurationChanged(configuration);
}
/**
@@ -509,7 +516,7 @@ public class VoiceInput implements OnClickListener {
mState = DEFAULT;
// Remove all pending tasks (e.g., timers to cancel voice input)
- mHandler.removeMessages(MSG_CLOSE_ERROR_DIALOG);
+ mHandler.removeMessages(MSG_RESET);
mSpeechRecognizer.cancel();
mUiListener.onCancelVoice();