diff options
author | 2011-01-19 03:34:48 -0800 | |
---|---|---|
committer | 2011-01-19 03:34:48 -0800 | |
commit | b7b83125544964bf128e07456edfd0170d889dc4 (patch) | |
tree | f508770da496b7969baed44975ee58fee9b81206 /java/src/com/android/inputmethod | |
parent | 7a614e9439c0ca752072bd357010517985cb9955 (diff) | |
parent | 9c383385d5fb82496d7e7ca57826fc21ec8a6bbd (diff) | |
download | latinime-b7b83125544964bf128e07456edfd0170d889dc4.tar.gz latinime-b7b83125544964bf128e07456edfd0170d889dc4.tar.xz latinime-b7b83125544964bf128e07456edfd0170d889dc4.zip |
Merge "Merge remote branch 'goog/master' into merge" into froyo-ub-latinimegoogle
Diffstat (limited to 'java/src/com/android/inputmethod')
21 files changed, 876 insertions, 378 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index ae1d302db..863421f18 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -330,6 +330,10 @@ public class Keyboard { return isAlphaKeyboard() && mShiftState.isManualTemporaryUpperCase(); } + public boolean isManualTemporaryUpperCaseFromAuto() { + return isAlphaKeyboard() && mShiftState.isManualTemporaryUpperCaseFromAuto(); + } + public KeyboardShiftState getKeyboardShiftState() { return mShiftState; } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardShiftState.java b/java/src/com/android/inputmethod/keyboard/KeyboardShiftState.java index 3e1eaf44e..d5412791d 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardShiftState.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardShiftState.java @@ -24,26 +24,38 @@ public class KeyboardShiftState { private static final int NORMAL = 0; private static final int MANUAL_SHIFTED = 1; - private static final int SHIFT_LOCKED = 2; + private static final int MANUAL_SHIFTED_FROM_AUTO = 2; private static final int AUTO_SHIFTED = 3; - private static final int SHIFT_LOCK_SHIFTED = 4; + private static final int SHIFT_LOCKED = 4; + private static final int SHIFT_LOCK_SHIFTED = 5; private int mState = NORMAL; public boolean setShifted(boolean newShiftState) { final int oldState = mState; if (newShiftState) { - if (oldState == NORMAL || oldState == AUTO_SHIFTED) { + switch (oldState) { + case NORMAL: mState = MANUAL_SHIFTED; - } else if (oldState == SHIFT_LOCKED) { + break; + case AUTO_SHIFTED: + mState = MANUAL_SHIFTED_FROM_AUTO; + break; + case SHIFT_LOCKED: mState = SHIFT_LOCK_SHIFTED; + break; } } else { - if (oldState == MANUAL_SHIFTED || oldState == AUTO_SHIFTED) { + switch (oldState) { + case MANUAL_SHIFTED: + case MANUAL_SHIFTED_FROM_AUTO: + case AUTO_SHIFTED: mState = NORMAL; - } else if (oldState == SHIFT_LOCK_SHIFTED) { + break; + case SHIFT_LOCK_SHIFTED: mState = SHIFT_LOCKED; - } + break; + } } if (DEBUG) Log.d(TAG, "setShifted(" + newShiftState + "): " + toString(oldState) + " > " + this); @@ -53,11 +65,21 @@ public class KeyboardShiftState { public void setShiftLocked(boolean newShiftLockState) { final int oldState = mState; if (newShiftLockState) { - if (oldState == NORMAL || oldState == MANUAL_SHIFTED || oldState == AUTO_SHIFTED) + switch (oldState) { + case NORMAL: + case MANUAL_SHIFTED: + case MANUAL_SHIFTED_FROM_AUTO: + case AUTO_SHIFTED: mState = SHIFT_LOCKED; + break; + } } else { - if (oldState == SHIFT_LOCKED || oldState == SHIFT_LOCK_SHIFTED) + switch (oldState) { + case SHIFT_LOCKED: + case SHIFT_LOCK_SHIFTED: mState = NORMAL; + break; + } } if (DEBUG) Log.d(TAG, "setShiftLocked(" + newShiftLockState + "): " + toString(oldState) @@ -84,7 +106,12 @@ public class KeyboardShiftState { } public boolean isManualTemporaryUpperCase() { - return mState == MANUAL_SHIFTED || mState == SHIFT_LOCK_SHIFTED; + return mState == MANUAL_SHIFTED || mState == MANUAL_SHIFTED_FROM_AUTO + || mState == SHIFT_LOCK_SHIFTED; + } + + public boolean isManualTemporaryUpperCaseFromAuto() { + return mState == MANUAL_SHIFTED_FROM_AUTO; } @Override @@ -96,8 +123,9 @@ public class KeyboardShiftState { switch (state) { case NORMAL: return "NORMAL"; case MANUAL_SHIFTED: return "MANUAL_SHIFTED"; - case SHIFT_LOCKED: return "SHIFT_LOCKED"; + case MANUAL_SHIFTED_FROM_AUTO: return "MANUAL_SHIFTED_FROM_AUTO"; case AUTO_SHIFTED: return "AUTO_SHIFTED"; + case SHIFT_LOCKED: return "SHIFT_LOCKED"; case SHIFT_LOCK_SHIFTED: return "SHIFT_LOCK_SHIFTED"; default: return "UKNOWN"; } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 331c8db6a..558de66a4 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -157,7 +157,15 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha boolean voiceButtonOnPrimary) { mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA; try { + if (mInputView == null) return; + final Keyboard oldKeyboard = mInputView.getKeyboard(); loadKeyboardInternal(mode, imeOptions, voiceKeyEnabled, voiceButtonOnPrimary, false); + final Keyboard newKeyboard = mInputView.getKeyboard(); + if (newKeyboard.isAlphaKeyboard()) { + final boolean localeChanged = (oldKeyboard == null) + || !newKeyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); + mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged); + } } catch (RuntimeException e) { Log.w(TAG, e); LatinImeLogger.logOnException(mode + "," + imeOptions, e); @@ -167,7 +175,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private void loadKeyboardInternal(int mode, int imeOptions, boolean voiceButtonEnabled, boolean voiceButtonOnPrimary, boolean isSymbols) { if (mInputView == null) return; - mInputView.setPreviewEnabled(mInputMethodService.getPopupOn()); mMode = mode; mImeOptions = imeOptions; @@ -176,13 +183,16 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha mIsSymbols = isSymbols; // Update the settings key state because number of enabled IMEs could have been changed mHasSettingsKey = getSettingsKeyMode(mPrefs, mInputMethodService); - makeSymbolsKeyboardIds(); + final KeyboardId id = getKeyboardId(mode, imeOptions, isSymbols); - KeyboardId id = getKeyboardId(mode, imeOptions, isSymbols); - LatinKeyboard keyboard = getKeyboard(id); + final Keyboard oldKeyboard = mInputView.getKeyboard(); + if (oldKeyboard != null && oldKeyboard.mId.equals(id)) + return; + makeSymbolsKeyboardIds(); mCurrentId = id; - mInputView.setKeyboard(keyboard); + mInputView.setPreviewEnabled(mInputMethodService.getPopupOn()); + mInputView.setKeyboard(getKeyboard(id)); } private LatinKeyboard getKeyboard(KeyboardId id) { @@ -210,6 +220,10 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive); keyboard.setShifted(false); + // If the cached keyboard had been switched to another keyboard while the language was + // displayed on its spacebar, it might have had arbitrary text fade factor. In such case, + // we should reset the text fade factor. + keyboard.setSpacebarTextFadeFactor(0.0f, null); return keyboard; } @@ -313,6 +327,13 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha return false; } + private boolean isManualTemporaryUpperCaseFromAuto() { + LatinKeyboard latinKeyboard = getLatinKeyboard(); + if (latinKeyboard != null) + return latinKeyboard.isManualTemporaryUpperCaseFromAuto(); + return false; + } + private void setManualTemporaryUpperCase(boolean shifted) { LatinKeyboard latinKeyboard = getLatinKeyboard(); if (latinKeyboard != null) { @@ -468,6 +489,10 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } else if (isShiftedOrShiftLocked() && shiftKeyState.isPressingOnShifted()) { // Shift has been pressed without chording while shifted state. toggleShift(); + } else if (isManualTemporaryUpperCaseFromAuto() && shiftKeyState.isPressing()) { + // Shift has been pressed without chording while manual temporary upper case + // transited from automatic temporary upper case. + toggleShift(); } } shiftKeyState.onRelease(); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index e7dd716fb..766fdf0e6 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -298,8 +298,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView); - LayoutInflater inflate = - (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); int previewLayout = 0; int keyTextSize = 0; @@ -365,7 +363,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { mPreviewPopup = new PopupWindow(context); if (previewLayout != 0) { - mPreviewText = (TextView) inflate.inflate(previewLayout, null); + mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null); mPreviewTextSizeLarge = (int) res.getDimension(R.dimen.key_preview_text_size_large); mPreviewPopup.setContentView(mPreviewText); mPreviewPopup.setBackgroundDrawable(null); @@ -912,9 +910,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { // We should re-draw popup preview when 1) we need to hide the preview, 2) we will show // the space key preview and 3) pointer moves off the space key to other letter key, we // should hide the preview of the previous key. - @SuppressWarnings("unused") final boolean hidePreviewOrShowSpaceKeyPreview = (tracker == null) - || (SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCHER + || (SubtypeSwitcher.getInstance().useSpacebarLanguageSwitcher() && SubtypeSwitcher.getInstance().needsToDisplayLanguage() && (tracker.isSpaceKey(keyIndex) || tracker.isSpaceKey(oldKeyIndex))); // If key changed and preview is on or the key is space (language switch is enabled) @@ -1081,9 +1078,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { private View inflateMiniKeyboardContainer(Key popupKey) { int popupKeyboardResId = mKeyboard.getPopupKeyboardResId(); - LayoutInflater inflater = (LayoutInflater)getContext().getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - View container = inflater.inflate(mPopupLayout, null); + View container = LayoutInflater.from(getContext()).inflate(mPopupLayout, null); if (container == null) throw new NullPointerException(); @@ -1410,6 +1405,12 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { mBuffer = null; mCanvas = null; mMiniKeyboardCache.clear(); + requestLayout(); + } + + public void purgeKeyboardAndClosing() { + mKeyboard = null; + closing(); } @Override diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java index 77dde8d1b..888375b93 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java @@ -24,6 +24,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.PorterDuff; @@ -47,7 +48,9 @@ public class LatinKeyboard extends Keyboard { private final Drawable mSpaceAutoCorrectionIndicator; private final Drawable mButtonArrowLeftIcon; private final Drawable mButtonArrowRightIcon; - private final int mSpaceBarTextShadowColor; + private final int mSpacebarTextColor; + private final int mSpacebarTextShadowColor; + private float mSpacebarTextFadeFactor = 0.0f; private final int[] mSpaceKeyIndexArray; private int mSpaceDragStartX; private int mSpaceDragLastDiff; @@ -80,11 +83,12 @@ public class LatinKeyboard extends Keyboard { super(context, id.getXmlId(), id); final Resources res = context.getResources(); mContext = context; + mSpacebarTextColor = res.getColor(R.color.latinkeyboard_bar_language_text); if (id.mColorScheme == KeyboardView.COLOR_SCHEME_BLACK) { - mSpaceBarTextShadowColor = res.getColor( + mSpacebarTextShadowColor = res.getColor( R.color.latinkeyboard_bar_language_shadow_black); } else { // default color scheme is KeyboardView.COLOR_SCHEME_WHITE - mSpaceBarTextShadowColor = res.getColor( + mSpacebarTextShadowColor = res.getColor( R.color.latinkeyboard_bar_language_shadow_white); } mSpaceAutoCorrectionIndicator = res.getDrawable(R.drawable.sym_keyboard_space_led); @@ -96,25 +100,38 @@ public class LatinKeyboard extends Keyboard { mSpaceKeyIndexArray = new int[] { indexOf(CODE_SPACE) }; } + public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboardView view) { + mSpacebarTextFadeFactor = fadeFactor; + updateSpacebarForLocale(false); + if (view != null) + view.invalidateKey(mSpaceKey); + } + + private static int getSpacebarTextColor(int color, float fadeFactor) { + final int newColor = Color.argb((int)(Color.alpha(color) * fadeFactor), + Color.red(color), Color.green(color), Color.blue(color)); + return newColor; + } + /** * @return a key which should be invalidated. */ public Key onAutoCorrectionStateChanged(boolean isAutoCorrection) { - updateSpaceBarForLocale(isAutoCorrection); + updateSpacebarForLocale(isAutoCorrection); return mSpaceKey; } - private void updateSpaceBarForLocale(boolean isAutoCorrection) { + private void updateSpacebarForLocale(boolean isAutoCorrection) { final Resources res = mContext.getResources(); // If application locales are explicitly selected. if (SubtypeSwitcher.getInstance().needsToDisplayLanguage()) { mSpaceKey.setIcon(new BitmapDrawable(res, - drawSpaceBar(OPACITY_FULLY_OPAQUE, isAutoCorrection))); + drawSpacebar(OPACITY_FULLY_OPAQUE, isAutoCorrection))); } else { // sym_keyboard_space_led can be shared with Black and White symbol themes. if (isAutoCorrection) { mSpaceKey.setIcon(new BitmapDrawable(res, - drawSpaceBar(OPACITY_FULLY_OPAQUE, isAutoCorrection))); + drawSpacebar(OPACITY_FULLY_OPAQUE, isAutoCorrection))); } else { mSpaceKey.setIcon(mSpaceIcon); } @@ -128,8 +145,8 @@ public class LatinKeyboard extends Keyboard { return bounds.width(); } - // Layout local language name and left and right arrow on space bar. - private static String layoutSpaceBar(Paint paint, Locale locale, Drawable lArrow, + // Layout local language name and left and right arrow on spacebar. + private static String layoutSpacebar(Paint paint, Locale locale, Drawable lArrow, Drawable rArrow, int width, int height, float origTextSize, boolean allowVariableTextSize) { final float arrowWidth = lArrow.getIntrinsicWidth(); @@ -138,7 +155,7 @@ public class LatinKeyboard extends Keyboard { final Rect bounds = new Rect(); // Estimate appropriate language name text size to fit in maxTextWidth. - String language = SubtypeSwitcher.getDisplayLanguage(locale); + String language = SubtypeSwitcher.getFullDisplayName(locale, true); int textWidth = getTextWidth(paint, language, origTextSize, bounds); // Assuming text width and text size are proportional to each other. float textSize = origTextSize * Math.min(maxTextWidth / textWidth, 1.0f); @@ -171,8 +188,7 @@ public class LatinKeyboard extends Keyboard { return language; } - @SuppressWarnings("unused") - private Bitmap drawSpaceBar(int opacity, boolean isAutoCorrection) { + private Bitmap drawSpacebar(int opacity, boolean isAutoCorrection) { final int width = mSpaceKey.mWidth; final int height = mSpaceIcon != null ? mSpaceIcon.getIntrinsicHeight() : mSpaceKey.mHeight; final Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); @@ -202,21 +218,25 @@ public class LatinKeyboard extends Keyboard { } final boolean allowVariableTextSize = true; - final String language = layoutSpaceBar(paint, subtypeSwitcher.getInputLocale(), + final String language = layoutSpacebar(paint, subtypeSwitcher.getInputLocale(), mButtonArrowLeftIcon, mButtonArrowRightIcon, width, height, getTextSizeFromTheme(textStyle, defaultTextSize), allowVariableTextSize); // Draw language text with shadow - final float baseline = height * SPACEBAR_LANGUAGE_BASELINE; + // In case there is no space icon, we will place the language text at the center of + // spacebar. final float descent = paint.descent(); - paint.setColor(mSpaceBarTextShadowColor); + final float textHeight = -paint.ascent() + descent; + final float baseline = (mSpaceIcon != null) ? height * SPACEBAR_LANGUAGE_BASELINE + : height / 2 + textHeight / 2; + paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, mSpacebarTextFadeFactor)); canvas.drawText(language, width / 2, baseline - descent - 1, paint); - paint.setColor(res.getColor(R.color.latinkeyboard_bar_language_text)); + paint.setColor(getSpacebarTextColor(mSpacebarTextColor, mSpacebarTextFadeFactor)); canvas.drawText(language, width / 2, baseline - descent, paint); // Put arrows that are already layed out on either side of the text - if (SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCHER + if (SubtypeSwitcher.getInstance().useSpacebarLanguageSwitcher() && subtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) { mButtonArrowLeftIcon.draw(canvas); mButtonArrowRightIcon.draw(canvas); @@ -291,7 +311,6 @@ public class LatinKeyboard extends Keyboard { * switching input languages. */ @Override - @SuppressWarnings("unused") // SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCHER is constant public boolean isInside(Key key, int pointX, int pointY) { int x = pointX; int y = pointY; @@ -302,7 +321,7 @@ public class LatinKeyboard extends Keyboard { if (code == CODE_DELETE) x -= key.mWidth / 6; } else if (code == CODE_SPACE) { y += LatinKeyboard.sSpacebarVerticalCorrection; - if (SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCHER + if (SubtypeSwitcher.getInstance().useSpacebarLanguageSwitcher() && SubtypeSwitcher.getInstance().getEnabledKeyboardLocaleCount() > 1) { if (mCurrentlyInSpace) { int diff = x - mSpaceDragStartX; diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index 51bfbf1f9..e9d5580e8 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -85,6 +85,14 @@ public class LatinKeyboardView extends KeyboardView { } } + public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboard oldKeyboard) { + final LatinKeyboard currentKeyboard = getLatinKeyboard(); + // We should not set text fade factor to the keyboard which does not display the language on + // its spacebar. + if (currentKeyboard != null && currentKeyboard == oldKeyboard) + currentKeyboard.setSpacebarTextFadeFactor(fadeFactor, this); + } + @Override protected boolean onLongPress(Key key) { int primaryCode = key.mCode; @@ -197,10 +205,10 @@ public class LatinKeyboardView extends KeyboardView { @Override public boolean onTouchEvent(MotionEvent me) { LatinKeyboard keyboard = getLatinKeyboard(); + if (keyboard == null) return true; // If there was a sudden jump, return without processing the actual motion event. - if (handleSuddenJump(me)) - return true; + if (handleSuddenJump(me)) return true; // Reset any bounding box controls in the keyboard if (me.getAction() == MotionEvent.ACTION_DOWN) { diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java index 458a9eed4..c150baadb 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java @@ -71,7 +71,7 @@ public class MiniKeyboardBuilder { for (CharSequence popupSpec : popupCharacters) { final CharSequence label = PopupCharactersParser.getLabel(popupSpec.toString()); // If the label is single letter, minKeyWidth is enough to hold the label. - if (label.length() > 1) { + if (label != null && label.length() > 1) { if (paint == null) { paint = new Paint(); paint.setAntiAlias(true); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index af08742d3..b6035e15e 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -42,6 +42,7 @@ public class BinaryDictionary extends Dictionary { private static final int TYPED_LETTER_MULTIPLIER = 2; + private static final BinaryDictionary sInstance = new BinaryDictionary(); private int mDicTypeId; private int mNativeDict; private long mDictLength; @@ -59,16 +60,24 @@ public class BinaryDictionary extends Dictionary { } } + private BinaryDictionary() { + } + /** - * Create a dictionary from a raw resource file + * Initialize a dictionary from a raw resource file * @param context application context for reading resources * @param resId the resource containing the raw binary dictionary + * @return initialized instance of BinaryDictionary */ - public BinaryDictionary(Context context, int resId, int dicTypeId) { - if (resId != 0) { - loadDictionary(context, resId); + public static BinaryDictionary initDictionary(Context context, int resId, int dicTypeId) { + synchronized (sInstance) { + sInstance.closeInternal(); + if (resId != 0) { + sInstance.loadDictionary(context, resId); + sInstance.mDicTypeId = dicTypeId; + } } - mDicTypeId = dicTypeId; + return sInstance; } private native int openNative(String sourceDir, long dictOffset, long dictSize, @@ -104,6 +113,8 @@ public class BinaryDictionary extends Dictionary { @Override public void getBigrams(final WordComposer codes, final CharSequence previousWord, final WordCallback callback, int[] nextLettersFrequencies) { + if (mNativeDict == 0) return; + char[] chars = previousWord.toString().toCharArray(); Arrays.fill(mOutputChars_bigrams, (char) 0); Arrays.fill(mFrequencies_bigrams, 0); @@ -135,6 +146,8 @@ public class BinaryDictionary extends Dictionary { @Override public void getWords(final WordComposer codes, final WordCallback callback, int[] nextLettersFrequencies) { + if (mNativeDict == 0) return; + final int codesSize = codes.size(); // Won't deal with really long words. if (codesSize > MAX_WORD_LENGTH - 1) return; @@ -179,6 +192,10 @@ public class BinaryDictionary extends Dictionary { @Override public synchronized void close() { + closeInternal(); + } + + private void closeInternal() { if (mNativeDict != 0) { closeNative(mNativeDict); mNativeDict = 0; @@ -188,7 +205,10 @@ public class BinaryDictionary extends Dictionary { @Override protected void finalize() throws Throwable { - close(); - super.finalize(); + try { + closeInternal(); + } finally { + super.finalize(); + } } } diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index 30f4a59f9..d2d1f22dd 100644 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -45,23 +45,22 @@ import android.widget.TextView; import java.util.ArrayList; public class CandidateView extends LinearLayout implements OnClickListener, OnLongClickListener { - private LatinIME mService; - private final ArrayList<View> mWords = new ArrayList<View>(); - private final TextView mPreviewText; - private final PopupWindow mPreviewPopup; - + private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); + private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); private static final int MAX_SUGGESTIONS = 16; + private final ArrayList<View> mWords = new ArrayList<View>(); private final boolean mConfigCandidateHighlightFontColorEnabled; + private final CharacterStyle mInvertedForegroundColorSpan; + private final CharacterStyle mInvertedBackgroundColorSpan; private final int mColorNormal; private final int mColorRecommended; private final int mColorOther; - private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); - private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); - private final CharacterStyle mInvertedForegroundColorSpan; - private final CharacterStyle mInvertedBackgroundColorSpan; + private final PopupWindow mPreviewPopup; + private final TextView mPreviewText; + private LatinIME mService; private SuggestedWords mSuggestions = SuggestedWords.EMPTY; private boolean mShowingAutoCorrectionInverted; private boolean mShowingAddToDictionary; @@ -186,9 +185,10 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo final TextView tv = (TextView)v.findViewById(R.id.candidate_word); final TextView dv = (TextView)v.findViewById(R.id.candidate_debug_info); tv.setTextColor(mColorNormal); + // TODO: Needs safety net? if (suggestions.mHasMinimalSuggestion - && ((i == 1 && !suggestions.mTypedWordValid) || - (i == 0 && suggestions.mTypedWordValid))) { + && ((i == 1 && !suggestions.mTypedWordValid) + || (i == 0 && suggestions.mTypedWordValid))) { final CharacterStyle style; if (mConfigCandidateHighlightFontColorEnabled) { style = BOLD_SPAN; @@ -329,7 +329,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo mService.pickSuggestionManually(index, word); } } - + @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); diff --git a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java index faee38eda..a9f2c2c22 100644 --- a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java +++ b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java @@ -107,7 +107,7 @@ public class InputLanguageSelection extends PreferenceActivity { res.updateConfiguration(conf, res.getDisplayMetrics()); int mainDicResId = LatinIME.getMainDictionaryResourceId(res); - BinaryDictionary bd = new BinaryDictionary(this, mainDicResId, Suggest.DIC_MAIN); + BinaryDictionary bd = BinaryDictionary.initDictionary(this, mainDicResId, Suggest.DIC_MAIN); // Is the dictionary larger than a placeholder? Arbitrarily chose a lower limit of // 4000-5000 words, whereas the LARGE_DICTIONARY is about 20000+ words. diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index ad7e4708a..b6042c769 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -22,6 +22,7 @@ import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.keyboard.LatinKeyboard; import com.android.inputmethod.keyboard.LatinKeyboardView; import com.android.inputmethod.latin.Utils.RingCharBuffer; import com.android.inputmethod.voice.VoiceIMEConnector; @@ -132,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; @@ -151,6 +155,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private boolean mAutoCap; private boolean mQuickFixes; private boolean mConfigSwipeDownDismissKeyboardEnabled; + private int mConfigDelayBeforeFadeoutLanguageOnSpacebar; + private int mConfigDurationOfFadeoutLanguageOnSpacebar; + private float mConfigFinalFadeoutFactorOfLanguageOnSpacebar; private int mCorrectionMode; private int mCommittedLength; @@ -160,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; @@ -241,9 +245,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int MSG_UPDATE_OLD_SUGGESTIONS = 1; private static final int MSG_UPDATE_SHIFT_STATE = 2; private static final int MSG_VOICE_RESULTS = 3; + private static final int MSG_FADEOUT_LANGUAGE_ON_SPACEBAR = 4; + private static final int MSG_DISMISS_LANGUAGE_ON_SPACEBAR = 5; @Override public void handleMessage(Message msg) { + final KeyboardSwitcher switcher = mKeyboardSwitcher; + final LatinKeyboardView inputView = switcher.getInputView(); switch (msg.what) { case MSG_UPDATE_SUGGESTIONS: updateSuggestions(); @@ -252,12 +260,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setOldSuggestions(); break; case MSG_UPDATE_SHIFT_STATE: - mKeyboardSwitcher.updateShiftState(); + switcher.updateShiftState(); break; case MSG_VOICE_RESULTS: mVoiceConnector.handleVoiceResults(preferCapitalization() - || (mKeyboardSwitcher.isAlphabetMode() - && mKeyboardSwitcher.isShiftedOrShiftLocked())); + || (switcher.isAlphabetMode() && switcher.isShiftedOrShiftLocked())); + break; + case MSG_FADEOUT_LANGUAGE_ON_SPACEBAR: + if (inputView != null) + inputView.setSpacebarTextFadeFactor( + (1.0f + mConfigFinalFadeoutFactorOfLanguageOnSpacebar) / 2, + (LatinKeyboard)msg.obj); + sendMessageDelayed(obtainMessage(MSG_DISMISS_LANGUAGE_ON_SPACEBAR, msg.obj), + mConfigDurationOfFadeoutLanguageOnSpacebar); + break; + case MSG_DISMISS_LANGUAGE_ON_SPACEBAR: + if (inputView != null) + inputView.setSpacebarTextFadeFactor( + mConfigFinalFadeoutFactorOfLanguageOnSpacebar, (LatinKeyboard)msg.obj); break; } } @@ -297,6 +317,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void updateVoiceResults() { sendMessage(obtainMessage(MSG_VOICE_RESULTS)); } + + public void startDisplayLanguageOnSpacebar(boolean localeChanged) { + removeMessages(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR); + removeMessages(MSG_DISMISS_LANGUAGE_ON_SPACEBAR); + final LatinKeyboardView inputView = mKeyboardSwitcher.getInputView(); + if (inputView != null) { + final LatinKeyboard keyboard = inputView.getLatinKeyboard(); + // The language is never displayed when the delay is zero. + if (mConfigDelayBeforeFadeoutLanguageOnSpacebar != 0) + inputView.setSpacebarTextFadeFactor(localeChanged ? 1.0f + : mConfigFinalFadeoutFactorOfLanguageOnSpacebar, keyboard); + // The language is always displayed when the delay is negative. + if (localeChanged && mConfigDelayBeforeFadeoutLanguageOnSpacebar > 0) { + sendMessageDelayed(obtainMessage(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR, keyboard), + mConfigDelayBeforeFadeoutLanguageOnSpacebar); + } + } + } } @Override @@ -315,10 +353,24 @@ 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( + R.integer.config_delay_before_fadeout_language_on_spacebar); + mConfigDurationOfFadeoutLanguageOnSpacebar = res.getInteger( + R.integer.config_duration_of_fadeout_language_on_spacebar); + mConfigFinalFadeoutFactorOfLanguageOnSpacebar = res.getInteger( + R.integer.config_final_fadeout_percentage_of_language_on_spacebar) / 100.0f; Utils.GCUtils.getInstance().reset(); boolean tryGC = true; @@ -360,7 +412,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSuggest.close(); } final SharedPreferences prefs = mPrefs; - mQuickFixes = prefs.getBoolean(Settings.PREF_QUICK_FIXES, true); + mQuickFixes = isQuickFixesEnabled(prefs); final Resources res = mResources; int mainDicResId = getMainDictionaryResourceId(res); @@ -402,27 +454,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onConfigurationChanged(Configuration conf) { mSubtypeSwitcher.onConfigurationChanged(conf); - if (mSubtypeSwitcher.isKeyboardMode()) - onKeyboardLanguageChanged(); - updateAutoTextEnabled(); - // If orientation changed while predicting, commit the change if (conf.orientation != mOrientation) { InputConnection ic = getCurrentInputConnection(); commitTyped(ic); if (ic != null) ic.finishComposingText(); // For voice input mOrientation = conf.orientation; - final int mode = mKeyboardSwitcher.getKeyboardMode(); - final EditorInfo attribute = getCurrentInputEditorInfo(); - final int imeOptions = (attribute != null) ? attribute.imeOptions : 0; - mKeyboardSwitcher.loadKeyboard(mode, imeOptions, - mVoiceConnector.isVoiceButtonEnabled(), - mVoiceConnector.isVoiceButtonOnPrimary()); } mConfigurationChanging = true; super.onConfigurationChanged(conf); - mVoiceConnector.onConfigurationChanged(mConfigurationChanging); + mVoiceConnector.onConfigurationChanged(conf); mConfigurationChanging = false; } @@ -473,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; @@ -525,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 { @@ -533,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(); } @@ -551,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(mKeyboardSwitcher.getInputView().getWindowToken()); - - if (TRACE) Debug.startMethodTracing("/data/trace/latinime"); + return mode; } private void checkReCorrectionOnStart() { @@ -1106,14 +1153,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void handleBackspace() { if (mVoiceConnector.logAndRevertVoiceInput()) return; - boolean deleteChar = false; - InputConnection ic = getCurrentInputConnection(); - if (ic == null) return; + final InputConnection ic = getCurrentInputConnection(); + if (ic == null) return; ic.beginBatchEdit(); mVoiceConnector.handleBackspace(); + boolean deleteChar = false; if (mHasValidSuggestions) { final int length = mComposing.length(); if (length > 0) { @@ -1131,12 +1178,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen deleteChar = true; } mHandler.postUpdateShiftKeyState(); + TextEntryState.backspace(); if (TextEntryState.getState() == TextEntryState.State.UNDO_COMMIT) { revertLastWord(deleteChar); ic.endBatchEdit(); return; - } else if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) { + } + + if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) { ic.deleteSurroundingText(mEnteredText.length(), 0); } else if (deleteChar) { if (mCandidateView != null && mCandidateView.dismissAddToDictionaryHint()) { @@ -1355,7 +1405,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private boolean isSuggestionsRequested() { - return mIsSettingsSuggestionStripOn; + return mIsSettingsSuggestionStripOn + && (mCorrectionMode > 0 || isShowingSuggestionsStrip()); } private boolean isShowingPunctuationList() { @@ -1491,7 +1542,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void showSuggestions(SuggestedWords suggestedWords, CharSequence typedWord) { setSuggestions(suggestedWords); if (suggestedWords.size() > 0) { - if (suggestedWords.hasAutoCorrectionWord()) { + if (Utils.shouldBlockedBySafetyNetForAutoCorrection(suggestedWords)) { + mBestWord = typedWord; + } else if (suggestedWords.hasAutoCorrectionWord()) { mBestWord = suggestedWords.getWord(1); } else { mBestWord = typedWord; @@ -1773,18 +1826,31 @@ 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); int toDelete = mCommittedLength; - CharSequence toTheLeft = ic.getTextBeforeCursor(mCommittedLength, 0); - if (toTheLeft != null && toTheLeft.length() > 0 - && isWordSeparator(toTheLeft.charAt(0))) { + final CharSequence toTheLeft = ic.getTextBeforeCursor(mCommittedLength, 0); + if (!TextUtils.isEmpty(toTheLeft) && isWordSeparator(toTheLeft.charAt(0))) { toDelete--; } ic.deleteSurroundingText(toDelete, 0); - ic.setComposingText(mComposing, 1); - TextEntryState.backspace(); + // 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); + 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(); + } mHandler.postUpdateSuggestions(); } else { sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); @@ -1814,21 +1880,22 @@ 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); } // "reset" and "next" are used only for USE_SPACEBAR_LANGUAGE_SWITCHER. private void toggleLanguage(boolean reset, boolean next) { - if (SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCHER) { + if (mSubtypeSwitcher.useSpacebarLanguageSwitcher()) { mSubtypeSwitcher.toggleLanguage(reset, next); } // 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()); @@ -2016,7 +2083,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mPopupOn = prefs.getBoolean(Settings.PREF_POPUP_ON, mResources.getBoolean(R.bool.config_default_popup_preview)); mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true); - mQuickFixes = prefs.getBoolean(Settings.PREF_QUICK_FIXES, true); + mQuickFixes = isQuickFixesEnabled(prefs); mAutoCorrectEnabled = isAutoCorrectEnabled(prefs); mBigramSuggestionEnabled = mAutoCorrectEnabled && isBigramSuggestionEnabled(prefs); @@ -2065,6 +2132,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSuggest.setAutoCorrectionThreshold(autoCorrectionThreshold); } + private boolean isQuickFixesEnabled(SharedPreferences sp) { + final boolean showQuickFixesOption = mResources.getBoolean( + R.bool.config_enable_quick_fixes_option); + if (!showQuickFixesOption) { + return isAutoCorrectEnabled(sp); + } + return sp.getBoolean(Settings.PREF_QUICK_FIXES, mResources.getBoolean( + R.bool.config_default_quick_fixes)); + } + private boolean isAutoCorrectEnabled(SharedPreferences sp) { final String currentAutoCorrectionSetting = sp.getString( Settings.PREF_AUTO_CORRECTION_THRESHOLD, @@ -2075,8 +2152,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private boolean isBigramSuggestionEnabled(SharedPreferences sp) { - // TODO: Define default value instead of 'true'. - return sp.getBoolean(Settings.PREF_BIGRAM_SUGGESTIONS, true); + final boolean showBigramSuggestionsOption = mResources.getBoolean( + R.bool.config_enable_bigram_suggestions_option); + if (!showBigramSuggestionsOption) { + return isAutoCorrectEnabled(sp); + } + return sp.getBoolean(Settings.PREF_BIGRAM_SUGGESTIONS, mResources.getBoolean( + R.bool.config_default_bigram_suggestions)); } private void initSuggestPuncList() { diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index d3fb23baf..c78e6dd07 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2008 The Android Open Source Project - * + * * 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 @@ -48,6 +48,7 @@ public class Settings extends PreferenceActivity DialogInterface.OnDismissListener, OnPreferenceClickListener { private static final String TAG = "Settings"; + public static final String PREF_GENERAL_SETTINGS_KEY = "general_settings"; public static final String PREF_VIBRATE_ON = "vibrate_on"; public static final String PREF_SOUND_ON = "sound_on"; public static final String PREF_POPUP_ON = "popup_on"; @@ -65,6 +66,8 @@ public class Settings extends PreferenceActivity public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold"; public static final String PREF_BIGRAM_SUGGESTIONS = "bigram_suggestion"; + public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; + // Dialog ids private static final int VOICE_INPUT_CONFIRM_DIALOG = 0; @@ -111,30 +114,64 @@ public class Settings extends PreferenceActivity mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTIONS); ensureConsistencyOfAutoCorrectionSettings(); + final PreferenceGroup generalSettings = + (PreferenceGroup) findPreference(PREF_GENERAL_SETTINGS_KEY); + final PreferenceGroup textCorrectionGroup = + (PreferenceGroup) findPreference(PREF_PREDICTION_SETTINGS_KEY); + final boolean showSettingsKeyOption = getResources().getBoolean( R.bool.config_enable_show_settings_key_option); if (!showSettingsKeyOption) { - getPreferenceScreen().removePreference(mSettingsKeyPreference); + generalSettings.removePreference(mSettingsKeyPreference); } final boolean showVoiceKeyOption = getResources().getBoolean( R.bool.config_enable_show_voice_key_option); if (!showVoiceKeyOption) { - getPreferenceScreen().removePreference(mVoicePreference); + generalSettings.removePreference(mVoicePreference); } Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); if (vibrator == null // @@@ || !vibrator.hasVibrator() ) { - getPreferenceScreen().removePreference( - getPreferenceScreen().findPreference(PREF_VIBRATE_ON)); + generalSettings.removePreference(findPreference(PREF_VIBRATE_ON)); } final boolean showSubtypeSettings = getResources().getBoolean( R.bool.config_enable_show_subtype_settings); if (!showSubtypeSettings) { - getPreferenceScreen().removePreference(findPreference(PREF_SUBTYPES)); + generalSettings.removePreference(findPreference(PREF_SUBTYPES)); + } + + final boolean showPopupOption = getResources().getBoolean( + R.bool.config_enable_show_popup_on_keypress_option); + if (!showPopupOption) { + generalSettings.removePreference(findPreference(PREF_POPUP_ON)); + } + + final boolean showRecorrectionOption = getResources().getBoolean( + R.bool.config_enable_show_recorrection_option); + if (!showRecorrectionOption) { + generalSettings.removePreference(findPreference(PREF_RECORRECTION_ENABLED)); + } + + final boolean showQuickFixesOption = getResources().getBoolean( + R.bool.config_enable_quick_fixes_option); + if (!showQuickFixesOption) { + textCorrectionGroup.removePreference(findPreference(PREF_QUICK_FIXES)); + } + + final boolean showBigramSuggestionsOption = getResources().getBoolean( + R.bool.config_enable_bigram_suggestions_option); + if (!showBigramSuggestionsOption) { + textCorrectionGroup.removePreference(findPreference(PREF_BIGRAM_SUGGESTIONS)); + } + + final boolean showUsabilityModeStudyOption = getResources().getBoolean( + R.bool.config_enable_usability_study_mode_option); + if (!showUsabilityModeStudyOption) { + getPreferenceScreen().removePreference(findPreference(PREF_USABILITY_STUDY_MODE)); } } @@ -184,7 +221,7 @@ public class Settings extends PreferenceActivity if (pref == mInputLanguageSelection) { final String action; if (android.os.Build.VERSION.SDK_INT - >= /* android.os.Build.VERSION_CODES.HONEYCOMB */ 10) { + >= /* android.os.Build.VERSION_CODES.HONEYCOMB */ 11) { action = "android.settings.INPUT_METHOD_AND_SUBTYPE_ENABLER"; } else { action = "com.android.inputmethod.latin.INPUT_LANGUAGE_SELECTION"; diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java new file mode 100644 index 000000000..917521c40 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * 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.latin; + +import android.content.Context; +import android.content.res.Resources; + +import java.util.Locale; + +public class SubtypeLocale { + private static String[] sExceptionKeys; + private static String[] sExceptionValues; + + private SubtypeLocale() { + // Intentional empty constructor for utility class. + } + + public static void init(Context context) { + final Resources res = context.getResources(); + sExceptionKeys = res.getStringArray(R.array.subtype_locale_exception_keys); + sExceptionValues = res.getStringArray(R.array.subtype_locale_exception_values); + } + + public static String getFullDisplayName(Locale locale) { + String localeCode = locale.toString(); + for (int index = 0; index < sExceptionKeys.length; index++) { + if (sExceptionKeys[index].equals(localeCode)) + return sExceptionValues[index]; + } + return locale.getDisplayName(locale); + } +} diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index cb676fa00..6666c8e15 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -41,11 +41,6 @@ import java.util.Locale; import java.util.Map; public class SubtypeSwitcher { - // TODO: This should be configurable by resource - // This flag indicates if we support language switching by swipe on space bar. - // We may or may not draw the current language on space bar regardless of this flag. - // @@@ - public static final boolean USE_SPACEBAR_LANGUAGE_SWITCHER = true; private static final boolean DBG = false; private static final String TAG = "SubtypeSwitcher"; @@ -64,6 +59,8 @@ public class SubtypeSwitcher { new ArrayList<InputMethodSubtype>(); private final ArrayList<String> mEnabledLanguagesOfCurrentInputMethod = new ArrayList<String>(); + private boolean mConfigUseSpacebarLanguageSwitcher; + /*-----------------------------------------------------------*/ // Variants which should be changed only by reload functions. private boolean mNeedsToDisplayLanguage; @@ -85,11 +82,9 @@ public class SubtypeSwitcher { public static void init(LatinIME service, SharedPreferences prefs) { sInstance.mPrefs = prefs; sInstance.resetParams(service); - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { - sInstance.initLanguageSwitcher(service); - } - sInstance.updateAllParameters(); + + SubtypeLocale.init(service); } private SubtypeSwitcher() { @@ -110,6 +105,10 @@ public class SubtypeSwitcher { mAllEnabledSubtypesOfCurrentInputMethod = null; // TODO: Voice input should be created here mVoiceInput = null; + mConfigUseSpacebarLanguageSwitcher = mResources.getBoolean( + R.bool.config_use_spacebar_language_switcher); + if (mConfigUseSpacebarLanguageSwitcher) + initLanguageSwitcher(service); } // Update all parameters stored in SubtypeSwitcher. @@ -123,8 +122,8 @@ public class SubtypeSwitcher { // Update parameters which are changed outside LatinIME. This parameters affect UI so they // should be updated every time onStartInputview. public void updateParametersOnStartInputView() { - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { - updateForSpaceBarLanguageSwitch(); + if (mConfigUseSpacebarLanguageSwitcher) { + updateForSpacebarLanguageSwitch(); } else { updateEnabledSubtypes(); } @@ -135,7 +134,7 @@ public class SubtypeSwitcher { private void updateEnabledSubtypes() { boolean foundCurrentSubtypeBecameDisabled = true; // @@@ mAllEnabledSubtypesOfCurrentInputMethod = mImm.getEnabledInputMethodSubtypeList( - //null, false); + // null, true); mEnabledLanguagesOfCurrentInputMethod.clear(); mEnabledKeyboardSubtypesOfCurrentInputMethod.clear(); for (InputMethodSubtype ims: mAllEnabledSubtypesOfCurrentInputMethod) { @@ -156,6 +155,7 @@ public class SubtypeSwitcher { && mIsSystemLanguageSameAsInputLanguage); if (foundCurrentSubtypeBecameDisabled) { if (DBG) { + Log.w(TAG, "Current subtype: " + mInputLocaleStr + ", " + mMode); Log.w(TAG, "Last subtype was disabled. Update to the current one."); } // @@@ updateSubtype(mImm.getCurrentInputMethodSubtype()); @@ -189,7 +189,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(); @@ -219,8 +219,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 @@ -312,19 +312,23 @@ public class SubtypeSwitcher { ////////////////////////////////// public int getEnabledKeyboardLocaleCount() { - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { + if (mConfigUseSpacebarLanguageSwitcher) { return mLanguageSwitcher.getLocaleCount(); } else { return mEnabledKeyboardSubtypesOfCurrentInputMethod.size(); } } + public boolean useSpacebarLanguageSwitcher() { + return mConfigUseSpacebarLanguageSwitcher; + } + public boolean needsToDisplayLanguage() { return mNeedsToDisplayLanguage; } public Locale getInputLocale() { - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { + if (mConfigUseSpacebarLanguageSwitcher) { return mLanguageSwitcher.getInputLocale(); } else { return mInputLocale; @@ -332,7 +336,7 @@ public class SubtypeSwitcher { } public String getInputLocaleStr() { - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { + if (mConfigUseSpacebarLanguageSwitcher) { String inputLanguage = null; inputLanguage = mLanguageSwitcher.getInputLanguage(); // Should return system locale if there is no Language available. @@ -346,7 +350,7 @@ public class SubtypeSwitcher { } public String[] getEnabledLanguages() { - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { + if (mConfigUseSpacebarLanguageSwitcher) { return mLanguageSwitcher.getEnabledLanguages(); } else { return mEnabledLanguagesOfCurrentInputMethod.toArray( @@ -355,7 +359,7 @@ public class SubtypeSwitcher { } public Locale getSystemLocale() { - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { + if (mConfigUseSpacebarLanguageSwitcher) { return mLanguageSwitcher.getSystemLocale(); } else { return mSystemLocale; @@ -363,7 +367,7 @@ public class SubtypeSwitcher { } public boolean isSystemLanguageSameAsInputLanguage() { - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { + if (mConfigUseSpacebarLanguageSwitcher) { return getSystemLocale().getLanguage().equalsIgnoreCase( getInputLocaleStr().substring(0, 2)); } else { @@ -375,7 +379,7 @@ public class SubtypeSwitcher { final Locale systemLocale = conf.locale; // If system configuration was changed, update all parameters. if (!TextUtils.equals(systemLocale.toString(), mSystemLocale.toString())) { - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { + if (mConfigUseSpacebarLanguageSwitcher) { // If the system locale changes and is different from the saved // locale (mSystemLocale), then reload the input locale list from the // latin ime settings (shared prefs) and reset the input locale @@ -389,7 +393,7 @@ public class SubtypeSwitcher { } public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { + if (mConfigUseSpacebarLanguageSwitcher) { if (Settings.PREF_SELECTED_LANGUAGES.equals(key)) { mLanguageSwitcher.loadLocales(sharedPreferences); } @@ -439,18 +443,18 @@ public class SubtypeSwitcher { private void triggerVoiceIME() { if (!mService.isInputViewShown()) return; VoiceIMEConnector.getInstance().startListening(false, - KeyboardSwitcher.getInstance().getInputView().getWindowToken(), false); + KeyboardSwitcher.getInstance().getInputView().getWindowToken()); } ////////////////////////////////////// - // SpaceBar Language Switch support // + // Spacebar Language Switch support // ////////////////////////////////////// private LanguageSwitcher mLanguageSwitcher; public static String getFullDisplayName(Locale locale, boolean returnsNameInThisLocale) { if (returnsNameInThisLocale) { - return toTitleCase(locale.getDisplayName(locale)); + return toTitleCase(SubtypeLocale.getFullDisplayName(locale)); } else { return toTitleCase(locale.getDisplayName()); } @@ -471,7 +475,7 @@ public class SubtypeSwitcher { return Character.toUpperCase(s.charAt(0)) + s.substring(1); } - private void updateForSpaceBarLanguageSwitch() { + private void updateForSpacebarLanguageSwitch() { // We need to update mNeedsToDisplayLanguage in onStartInputView because // getEnabledKeyboardLocaleCount could have been changed. mNeedsToDisplayLanguage = !(getEnabledKeyboardLocaleCount() <= 1 @@ -484,7 +488,7 @@ public class SubtypeSwitcher { } public String getNextInputLanguageName() { - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { + if (mConfigUseSpacebarLanguageSwitcher) { return getDisplayLanguage(mLanguageSwitcher.getNextInputLocale()); } else { return ""; @@ -492,7 +496,7 @@ public class SubtypeSwitcher { } public String getPreviousInputLanguageName() { - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { + if (mConfigUseSpacebarLanguageSwitcher) { return getDisplayLanguage(mLanguageSwitcher.getPrevInputLocale()); } else { return ""; @@ -529,13 +533,13 @@ public class SubtypeSwitcher { } public void loadSettings() { - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { + if (mConfigUseSpacebarLanguageSwitcher) { mLanguageSwitcher.loadLocales(mPrefs); } } public void toggleLanguage(boolean reset, boolean next) { - if (USE_SPACEBAR_LANGUAGE_SWITCHER) { + if (mConfigUseSpacebarLanguageSwitcher) { if (reset) { mLanguageSwitcher.reset(); } else { diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 9ea9c2f3e..24c73e8ea 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -31,7 +31,7 @@ import java.util.Arrays; */ public class Suggest implements Dictionary.WordCallback { - public static final String TAG = "Suggest"; + public static final String TAG = Suggest.class.getSimpleName(); public static final int APPROX_MAX_WORD_LENGTH = 32; @@ -64,6 +64,8 @@ public class Suggest implements Dictionary.WordCallback { static final int LARGE_DICTIONARY_THRESHOLD = 200 * 1000; + private static boolean DBG = LatinImeLogger.sDBG; + private BinaryDictionary mMainDict; private Dictionary mUserDictionary; @@ -93,7 +95,7 @@ public class Suggest implements Dictionary.WordCallback { private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>(); ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>(); private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>(); - private boolean mHaveCorrection; + private boolean mHaveAutoCorrection; private String mLowerOriginalWord; // TODO: Remove these member variables by passing more context to addWord() callback method @@ -103,7 +105,7 @@ public class Suggest implements Dictionary.WordCallback { private int mCorrectionMode = CORRECTION_BASIC; public Suggest(Context context, int dictionaryResId) { - mMainDict = new BinaryDictionary(context, dictionaryResId, DIC_MAIN); + mMainDict = BinaryDictionary.initDictionary(context, dictionaryResId, DIC_MAIN); initPool(); } @@ -127,7 +129,7 @@ public class Suggest implements Dictionary.WordCallback { } public boolean hasMainDictionary() { - return mMainDict.getSize() > LARGE_DICTIONARY_THRESHOLD; + return mMainDict != null && mMainDict.getSize() > LARGE_DICTIONARY_THRESHOLD; } public int getApproxMaxWordLength() { @@ -198,7 +200,7 @@ public class Suggest implements Dictionary.WordCallback { public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer, CharSequence prevWordForBigram) { LatinImeLogger.onStartSuggestion(prevWordForBigram); - mHaveCorrection = false; + mHaveAutoCorrection = false; mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); mIsAllUpperCase = wordComposer.isAllUpperCase(); collectGarbage(mSuggestions, mPrefMaxSuggestions); @@ -273,10 +275,13 @@ public class Suggest implements Dictionary.WordCallback { if (mSuggestions.size() > 0 && isValidWord(typedWord) && (mCorrectionMode == CORRECTION_FULL || mCorrectionMode == CORRECTION_FULL_BIGRAM)) { - mHaveCorrection = true; + if (DBG) { + Log.d(TAG, "Auto corrected by CORRECTION_FULL."); + } + mHaveAutoCorrection = true; } } - mMainDict.getWords(wordComposer, this, mNextLettersFrequencies); + if (mMainDict != null) mMainDict.getWords(wordComposer, this, mNextLettersFrequencies); if ((mCorrectionMode == CORRECTION_FULL || mCorrectionMode == CORRECTION_FULL_BIGRAM) && mSuggestions.size() > 0 && mPriorities.length > 0) { // TODO: when the normalized score of the first suggestion is nearly equals to @@ -289,7 +294,10 @@ public class Suggest implements Dictionary.WordCallback { + "(" + mAutoCorrectionThreshold + ")"); } if (normalizedScore >= mAutoCorrectionThreshold) { - mHaveCorrection = true; + if (DBG) { + Log.d(TAG, "Auto corrected by S-threthhold."); + } + mHaveAutoCorrection = true; } } } @@ -331,7 +339,10 @@ public class Suggest implements Dictionary.WordCallback { canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i + 1)); } if (canAdd) { - mHaveCorrection = true; + if (DBG) { + Log.d(TAG, "Auto corrected by AUTOTEXT."); + } + mHaveAutoCorrection = true; mSuggestions.add(i + 1, autoText); i++; } @@ -374,7 +385,7 @@ public class Suggest implements Dictionary.WordCallback { } public boolean hasMinimalCorrection() { - return mHaveCorrection; + return mHaveAutoCorrection; } private boolean compareCaseInsensitive(final String mLowerOriginalWord, @@ -496,7 +507,7 @@ public class Suggest implements Dictionary.WordCallback { } public boolean isValidWord(final CharSequence word) { - if (word == null || word.length() == 0) { + if (word == null || word.length() == 0 || mMainDict == null) { return false; } return mMainDict.isValidWord(word) 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/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index 56ad6c7aa..160948507 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -36,6 +36,8 @@ import java.text.SimpleDateFormat; import java.util.Date; public class Utils { + private static final String TAG = Utils.class.getSimpleName(); + private static boolean DBG = LatinImeLogger.sDBG; /** * Cancel an {@link AsyncTask}. @@ -96,6 +98,29 @@ public class Utils { // || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1; } + + public static boolean shouldBlockedBySafetyNetForAutoCorrection(SuggestedWords suggestions) { + // Safety net for auto correction. + // Actually if we hit this safety net, it's actually a bug. + if (suggestions.size() <= 1 || suggestions.mTypedWordValid) return false; + CharSequence typedWord = suggestions.getWord(0); + CharSequence candidateWord = suggestions.getWord(1); + final int typedWordLength = typedWord.length(); + final int maxEditDistanceOfNativeDictionary = typedWordLength < 5 ? 2 : typedWordLength / 2; + final int distance = Utils.editDistance(typedWord, candidateWord); + if (DBG) { + Log.d(TAG, "Autocorrected edit distance = " + distance + + ", " + maxEditDistanceOfNativeDictionary); + } + if (distance > maxEditDistanceOfNativeDictionary) { + Log.w(TAG, "(Error) The edit distance of this correction exceeds limit. " + + "Turning off auto-correction."); + return true; + } else { + return false; + } + } + /* package */ static class RingCharBuffer { private static RingCharBuffer sRingCharBuffer = new RingCharBuffer(); private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC'; diff --git a/java/src/com/android/inputmethod/voice/RecognitionView.java b/java/src/com/android/inputmethod/voice/RecognitionView.java index 12d0de852..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,81 +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(); LayoutInflater inflater = (LayoutInflater) context.getSystemService( - Context.LAYOUT_INFLATER_SERVICE); + Context.LAYOUT_INFLATER_SERVICE); + mView = inflater.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); + + 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; } @@ -146,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); } } }); @@ -158,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(); @@ -207,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); } /** @@ -248,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; @@ -259,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); @@ -281,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; @@ -295,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 1ff918315..fe6e318c9 100644 --- a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java +++ b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java @@ -16,6 +16,7 @@ package com.android.inputmethod.voice; +import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.latin.EditingUtils; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinIME.UIHandler; @@ -29,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; @@ -77,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; @@ -91,7 +96,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { private boolean mVoiceInputHighlighted; private InputMethodManager mImm; - private LatinIME mContext; + private LatinIME mService; private AlertDialog mVoiceWarningDialog; private VoiceInput mVoiceInput; private final VoiceResults mVoiceResults = new VoiceResults(); @@ -111,21 +116,19 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { return sInstance; } - private void initInternal(LatinIME context, SharedPreferences prefs, UIHandler h) { - mContext = context; + private void initInternal(LatinIME service, SharedPreferences prefs, UIHandler h) { + mService = service; mHandler = h; - mImm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + mImm = (InputMethodManager) service.getSystemService(Context.INPUT_METHOD_SERVICE); mSubtypeSwitcher = SubtypeSwitcher.getInstance(); if (VOICE_INSTALLED) { - mVoiceInput = new VoiceInput(context, this); - mHints = new Hints(context, prefs, new Hints.Display() { + mVoiceInput = new VoiceInput(service, this); + mHints = new Hints(service, prefs, new Hints.Display() { @Override 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); + View view = LayoutInflater.from(mService).inflate(viewResource, null); + mService.setCandidatesView(view); + mService.setCandidatesViewShown(true); mIsShowingHint = true; } }); @@ -161,23 +164,22 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { mVoiceInput.flushAllTextModificationCounters(); // send this intent AFTER logging any prior aggregated edits. mVoiceInput.logTextModifiedByChooseSuggestion(suggestion.toString(), index, - wordSeparators, mContext.getCurrentInputConnection()); + wordSeparators, mService.getCurrentInputConnection()); } } - 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; } - AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + AlertDialog.Builder builder = new AlertDialog.Builder(mService); builder.setCancelable(true); builder.setIcon(R.drawable.ic_mic_dialog); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { mVoiceInput.logKeyboardWarningDialogOk(); - reallyStartListening(swipe, configurationChanging); + reallyStartListening(swipe); } }); builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @@ -199,13 +201,13 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { final CharSequence message; if (mLocaleSupportedForVoiceInput) { message = TextUtils.concat( - mContext.getText(R.string.voice_warning_may_not_understand), "\n\n", - mContext.getText(R.string.voice_warning_how_to_turn_off)); + mService.getText(R.string.voice_warning_may_not_understand), "\n\n", + mService.getText(R.string.voice_warning_how_to_turn_off)); } else { message = TextUtils.concat( - mContext.getText(R.string.voice_warning_locale_not_supported), "\n\n", - mContext.getText(R.string.voice_warning_may_not_understand), "\n\n", - mContext.getText(R.string.voice_warning_how_to_turn_off)); + mService.getText(R.string.voice_warning_locale_not_supported), "\n\n", + mService.getText(R.string.voice_warning_may_not_understand), "\n\n", + mService.getText(R.string.voice_warning_how_to_turn_off)); } builder.setMessage(message); @@ -296,7 +298,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { } public void showPunctuationHintIfNecessary() { - InputConnection ic = mContext.getCurrentInputConnection(); + InputConnection ic = mService.getCurrentInputConnection(); if (!mImmediatelyAfterVoiceInput && mAfterVoiceInput && ic != null) { if (mHints.showPunctuationHintIfNecessary(ic)) { mVoiceInput.logPunctuationHintDisplayed(); @@ -364,17 +366,17 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { } private void revertVoiceInput() { - InputConnection ic = mContext.getCurrentInputConnection(); + InputConnection ic = mService.getCurrentInputConnection(); if (ic != null) ic.commitText("", 1); - mContext.updateSuggestions(); + mService.updateSuggestions(); mVoiceInputHighlighted = false; } public void commitVoiceInput() { if (VOICE_INSTALLED && mVoiceInputHighlighted) { - InputConnection ic = mContext.getCurrentInputConnection(); + InputConnection ic = mService.getCurrentInputConnection(); if (ic != null) ic.finishComposingText(); - mContext.updateSuggestions(); + mService.updateSuggestions(); mVoiceInputHighlighted = false; } } @@ -394,7 +396,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { if (mShowingVoiceSuggestions) { // Retain the replaced word in the alternatives array. String wordToBeReplaced = EditingUtils.getWordAtCursor( - mContext.getCurrentInputConnection(), wordSeparators); + mService.getCurrentInputConnection(), wordSeparators); if (!mWordToSuggestions.containsKey(wordToBeReplaced)) { wordToBeReplaced = wordToBeReplaced.toLowerCase(); } @@ -438,8 +440,8 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { builder.addWords(suggestions); } builder.setTypedWordValid(true).setHasMinimalSuggestion(true); - mContext.setSuggestions(builder.build()); - mContext.setCandidatesViewShown(true); + mService.setSuggestions(builder.build()); + mService.setCandidatesViewShown(true); return true; } return false; @@ -486,15 +488,15 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { mAfterVoiceInput = true; mImmediatelyAfterVoiceInput = true; - InputConnection ic = mContext.getCurrentInputConnection(); - if (!mContext.isFullscreenMode()) { + InputConnection ic = mService.getCurrentInputConnection(); + if (!mService.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(); + mService.vibrate(); final List<CharSequence> nBest = new ArrayList<CharSequence>(); for (String c : mVoiceResults.candidates) { @@ -511,7 +513,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { mHints.registerVoiceResult(bestResult); if (ic != null) ic.beginBatchEdit(); // To avoid extra updates on committing older text - mContext.commitTyped(ic); + mService.commitTyped(ic); EditingUtils.appendText(ic, bestResult); if (ic != null) ic.endBatchEdit(); @@ -520,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() { - mContext.setCandidatesViewShown(false); + 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); } - mContext.setInputView(v); - mContext.updateInputViewShown(); - if (configChanged) { - mVoiceInput.onConfigurationChanged(); + + 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 (configuration != null) { + mVoiceInput.onConfigurationChanged(configuration); } }}); } @@ -547,7 +565,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { */ } - private void reallyStartListening(boolean swipe, final boolean configurationChanging) { + private void reallyStartListening(boolean swipe) { if (!VOICE_INSTALLED) { return; } @@ -555,7 +573,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { // 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(); + PreferenceManager.getDefaultSharedPreferences(mService).edit(); editor.putBoolean(PREF_HAS_USED_VOICE_INPUT, true); SharedPreferencesCompat.apply(editor); mHasUsedVoiceInput = true; @@ -565,34 +583,32 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { // 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(); + PreferenceManager.getDefaultSharedPreferences(mService).edit(); editor.putBoolean(PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE, true); SharedPreferencesCompat.apply(editor); mHasUsedVoiceInputUnsupportedLocale = true; } // Clear N-best suggestions - mContext.clearSuggestions(); + mService.clearSuggestions(); 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 @@ -603,7 +619,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { return ENABLE_VOICE_BUTTON && fieldCanDoVoice(fieldContext) && !(attribute != null && IME_OPTION_NO_MICROPHONE.equals(attribute.privateImeOptions)) - && SpeechRecognizer.isRecognitionAvailable(mContext); + && SpeechRecognizer.isRecognitionAvailable(mService); } public void loadSettings(EditorInfo attribute, SharedPreferences sp) { @@ -616,10 +632,10 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { 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)) + mService.getString(R.string.voice_mode_main)); + mVoiceButtonEnabled = !voiceMode.equals(mService.getString(R.string.voice_mode_off)) && shouldShowVoiceButton(makeFieldContext(), attribute); - mVoiceButtonOnPrimary = voiceMode.equals(mContext.getString(R.string.voice_mode_main)); + mVoiceButtonOnPrimary = voiceMode.equals(mService.getString(R.string.voice_mode_main)); } } @@ -632,9 +648,14 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { public void onStartInputView(IBinder token) { // If IME is in voice mode, but still needs to show the voice warning dialog, // keep showing the warning. - if (mSubtypeSwitcher.isVoiceMode() && needsToShowWarningDialog() && token != null) { - showVoiceWarningDialog(false, token, false); + if (mSubtypeSwitcher.isVoiceMode() && token != null) { + // Close keyboard view if it is been shown. + if (KeyboardSwitcher.getInstance().isInputViewShown()) + KeyboardSwitcher.getInstance().getInputView().purgeKeyboardAndClosing(); + startListening(false, token); } + // If we have no token, onAttachedToWindow will take care of showing dialog and start + // listening. } public void onAttachedToWindow() { @@ -643,9 +664,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); } } @@ -664,7 +685,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { // onCurrentInputMethodSubtypeChanged() will be called first. LatinIME will know // that it's in keyboard mode and SubtypeSwitcher will call onCancelVoice(). mRecognizing = false; - mContext.switchToKeyboardView(); + mService.switchToKeyboardView(); } } } @@ -682,8 +703,8 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { public FieldContext makeFieldContext() { SubtypeSwitcher switcher = SubtypeSwitcher.getInstance(); - return new FieldContext(mContext.getCurrentInputConnection(), - mContext.getCurrentInputEditorInfo(), switcher.getInputLocaleStr(), + return new FieldContext(mService.getCurrentInputConnection(), + mService.getCurrentInputEditorInfo(), switcher.getInputLocaleStr(), switcher.getEnabledLanguages()); } 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(); |