diff options
Diffstat (limited to 'java/src')
13 files changed, 239 insertions, 227 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index e7a9d8513..3979fb53e 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -17,6 +17,7 @@ package com.android.inputmethod.keyboard; import java.util.Arrays; +import java.util.HashMap; import java.util.List; public abstract class KeyDetector { @@ -108,4 +109,31 @@ public abstract class KeyDetector { * @return The nearest key index */ abstract public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys); + + /** + * Compute the most common key width in order to use it as proximity key detection threshold. + * + * @param keyboard The keyboard to compute the most common key width + * @return The most common key width in the keyboard + */ + public static int getMostCommonKeyWidth(Keyboard keyboard) { + if (keyboard == null) return 0; + final List<Key> keys = keyboard.getKeys(); + if (keys == null || keys.size() == 0) return 0; + final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>(); + int maxCount = 0; + int mostCommonWidth = 0; + for (Key key : keys) { + final Integer width = key.mWidth + key.mGap; + Integer count = histogram.get(width); + if (count == null) + count = 0; + histogram.put(width, ++count); + if (count > maxCount) { + maxCount = count; + mostCommonWidth = width; + } + } + return mostCommonWidth; + } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 19f1fa8ee..d4c5e579b 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -68,8 +68,7 @@ import java.util.WeakHashMap; * @attr ref R.styleable#KeyboardView_popupLayout */ public class KeyboardView extends View implements PointerTracker.UIProxy { - private static final String TAG = "KeyboardView"; - private static final boolean DEBUG = false; + private static final String TAG = KeyboardView.class.getSimpleName(); private static final boolean DEBUG_SHOW_ALIGN = false; private static final boolean DEBUG_KEYBOARD_GRID = false; @@ -115,7 +114,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { private int[] mOffsetInWindow; private int mOldPreviewKeyIndex = KeyDetector.NOT_A_KEY; private boolean mShowPreview = true; - private boolean mShowTouchPoints = true; private int mPopupPreviewOffsetX; private int mPopupPreviewOffsetY; private int mWindowY; @@ -158,18 +156,20 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { // Drawing /** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/ private boolean mDrawPending; + /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */ + private boolean mKeyboardChanged; /** The dirty region in the keyboard bitmap */ private final Rect mDirtyRect = new Rect(); + /** The key to invalidate. */ + private Key mInvalidatedKey; + /** The dirty region for single key drawing */ + private final Rect mInvalidatedKeyRect = new Rect(); /** The keyboard bitmap for faster updates */ private Bitmap mBuffer; - /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */ - private boolean mKeyboardChanged; - private Key mInvalidatedKey; /** The canvas for the above mutable keyboard bitmap */ private Canvas mCanvas; private final Paint mPaint; private final Rect mPadding; - private final Rect mClipRegion = new Rect(0, 0, 0, 0); // This map caches key label text height in pixel as value and key label text size as map key. private final HashMap<Integer, Integer> mTextHeightCache = new HashMap<Integer, Integer>(); // Distance from horizontal center of the key, proportional to key label text height and width. @@ -506,10 +506,9 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { tracker.setKeyboard(keyboard, mKeys, mKeyHysteresisDistance); } requestLayout(); - // Hint to reallocate the buffer if the size changed mKeyboardChanged = true; invalidateAllKeys(); - computeProximityThreshold(keyboard, mKeys); + mKeyDetector.setProximityThreshold(KeyDetector.getMostCommonKeyWidth(keyboard)); mMiniKeyboardCache.clear(); } @@ -601,37 +600,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { } } - /** - * Compute the most common key width and use it as proximity key detection threshold. - * @param keyboard - * @param keys - */ - private void computeProximityThreshold(Keyboard keyboard, Key[] keys) { - if (keyboard == null || keys == null || keys.length == 0) return; - final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>(); - int maxCount = 0; - int mostCommonWidth = 0; - for (Key key : keys) { - final Integer width = key.mWidth + key.mGap; - Integer count = histogram.get(width); - if (count == null) - count = 0; - histogram.put(width, ++count); - if (count > maxCount) { - maxCount = count; - mostCommonWidth = width; - } - } - mKeyDetector.setProximityThreshold(mostCommonWidth); - } - - @Override - public void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - // Release the buffer, if any and it will be reallocated on the next draw - mBuffer = null; - } - @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); @@ -641,19 +609,18 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { canvas.drawBitmap(mBuffer, 0, 0, null); } - @SuppressWarnings("unused") private void onBufferDraw() { + final int width = getWidth(); + final int height = getHeight(); + if (width == 0 || height == 0) + return; if (mBuffer == null || mKeyboardChanged) { - if (mBuffer == null || mKeyboardChanged && - (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) { - // Make sure our bitmap is at least 1x1 - final int width = Math.max(1, getWidth()); - final int height = Math.max(1, getHeight()); - mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - mCanvas = new Canvas(mBuffer); - } - invalidateAllKeys(); mKeyboardChanged = false; + mDirtyRect.union(0, 0, width, height); + } + if (mBuffer == null || mBuffer.getWidth() != width || mBuffer.getHeight() != height) { + mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + mCanvas = new Canvas(mBuffer); } final Canvas canvas = mCanvas; canvas.clipRect(mDirtyRect, Op.REPLACE); @@ -662,30 +629,19 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { final Paint paint = mPaint; final Drawable keyBackground = mKeyBackground; - final Rect clipRegion = mClipRegion; final Rect padding = mPadding; final int kbdPaddingLeft = getPaddingLeft(); final int kbdPaddingTop = getPaddingTop(); final Key[] keys = mKeys; - final Key invalidKey = mInvalidatedKey; final boolean isManualTemporaryUpperCase = mKeyboard.isManualTemporaryUpperCase(); + final boolean drawSingleKey = (mInvalidatedKey != null + && mInvalidatedKeyRect.contains(mDirtyRect)); - boolean drawSingleKey = false; - if (invalidKey != null && canvas.getClipBounds(clipRegion)) { - // TODO we should use Rect.inset and Rect.contains here. - // Is clipRegion completely contained within the invalidated key? - if (invalidKey.mX + kbdPaddingLeft - 1 <= clipRegion.left && - invalidKey.mY + kbdPaddingTop - 1 <= clipRegion.top && - invalidKey.mX + invalidKey.mWidth + kbdPaddingLeft + 1 >= clipRegion.right && - invalidKey.mY + invalidKey.mHeight + kbdPaddingTop + 1 >= clipRegion.bottom) { - drawSingleKey = true; - } - } canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); final int keyCount = keys.length; for (int i = 0; i < keyCount; i++) { final Key key = keys[i]; - if (drawSingleKey && invalidKey != key) { + if (drawSingleKey && key != mInvalidatedKey) { continue; } int[] drawableState = key.getCurrentDrawableState(); @@ -739,8 +695,10 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { } else { positionX = (key.mWidth + padding.left - padding.right) / 2; paint.setTextAlign(Align.CENTER); - if (DEBUG_SHOW_ALIGN && label.length() > 1) - drawVerticalLine(canvas, positionX, rowHeight, 0xc0008080, new Paint()); + if (DEBUG_SHOW_ALIGN) { + if (label.length() > 1) + drawVerticalLine(canvas, positionX, rowHeight, 0xc0008080, new Paint()); + } } if (key.mManualTemporaryUpperCaseHintIcon != null && isManualTemporaryUpperCase) { paint.setColor(mKeyTextColorDisabled); @@ -811,32 +769,13 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { canvas.drawLine(0, i * ch, cw * mKeyboard.GRID_WIDTH, i * ch, p); } - mInvalidatedKey = null; // Overlay a dark rectangle to dim the keyboard if (mMiniKeyboardView != null) { paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24); - canvas.drawRect(0, 0, getWidth(), getHeight(), paint); - } - - if (DEBUG) { - if (mShowTouchPoints) { - for (PointerTracker tracker : mPointerTrackers) { - int startX = tracker.getStartX(); - int startY = tracker.getStartY(); - int lastX = tracker.getLastX(); - int lastY = tracker.getLastY(); - paint.setAlpha(128); - paint.setColor(0xFFFF0000); - canvas.drawCircle(startX, startY, 3, paint); - canvas.drawLine(startX, startY, lastX, lastY, paint); - paint.setColor(0xFF0000FF); - canvas.drawCircle(lastX, lastY, 3, paint); - paint.setColor(0xFF00FF00); - canvas.drawCircle((startX + lastX) / 2, (startY + lastY) / 2, 2, paint); - } - } + canvas.drawRect(0, 0, width, height, paint); } + mInvalidatedKey = null; mDrawPending = false; mDirtyRect.setEmpty(); } @@ -1050,12 +989,11 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { if (key == null) return; mInvalidatedKey = key; - // TODO we should clean up this and record key's region to use in onBufferDraw. - mDirtyRect.union(key.mX + getPaddingLeft(), key.mY + getPaddingTop(), - key.mX + key.mWidth + getPaddingLeft(), key.mY + key.mHeight + getPaddingTop()); + mInvalidatedKeyRect.set(0, 0, key.mWidth, key.mHeight); + mInvalidatedKeyRect.offset(key.mX + getPaddingLeft(), key.mY + getPaddingTop()); + mDirtyRect.union(mInvalidatedKeyRect); onBufferDraw(); - invalidate(key.mX + getPaddingLeft(), key.mY + getPaddingTop(), - key.mX + key.mWidth + getPaddingLeft(), key.mY + key.mHeight + getPaddingTop()); + invalidate(mInvalidatedKeyRect); } private boolean openPopupIfRequired(int keyIndex, PointerTracker tracker) { diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index af2fd5ce1..e7246dd6e 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -141,6 +141,9 @@ public class LatinKeyboardView extends KeyboardView { * KeyboardView. */ private boolean handleSuddenJump(MotionEvent me) { + // If device has distinct multi touch panel, there is no need to check sudden jump. + if (hasDistinctMultitouch()) + return false; final int action = me.getAction(); final int x = (int) me.getX(); final int y = (int) me.getY(); diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index a981f724f..4c90e2c3f 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -490,15 +490,6 @@ public class PointerTracker { return mKeyState.getDownTime(); } - // These package scope methods are only for debugging purpose. - /* package */ int getStartX() { - return mKeyState.getStartX(); - } - - /* package */ int getStartY() { - return mKeyState.getStartY(); - } - private boolean isMinorMoveBounce(int x, int y, int newKey) { if (mKeys == null || mKeyHysteresisDistanceSquared < 0) throw new IllegalStateException("keyboard and/or hysteresis not set"); diff --git a/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java b/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java index 250bb95eb..a62ed96a3 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java @@ -23,8 +23,6 @@ package com.android.inputmethod.keyboard; private final KeyDetector mKeyDetector; // The position and time at which first down event occurred. - private int mStartX; - private int mStartY; private long mDownTime; private long mUpTime; @@ -54,14 +52,6 @@ package com.android.inputmethod.keyboard; return mKeyY; } - public int getStartX() { - return mStartX; - } - - public int getStartY() { - return mStartY; - } - public long getDownTime() { return mDownTime; } @@ -79,8 +69,6 @@ package com.android.inputmethod.keyboard; } public int onDownKey(int x, int y, long eventTime) { - mStartX = x; - mStartY = y; mDownTime = eventTime; return onMoveToNewKey(onMoveKeyInternal(x, y), x, y); } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 813f7d32f..a7e95a080 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.AssetFileDescriptor; import android.util.Log; +import java.io.File; import java.util.Arrays; /** @@ -72,9 +73,40 @@ public class BinaryDictionary extends Dictionary { public static BinaryDictionary initDictionary(Context context, int resId, int dicTypeId) { synchronized (sInstance) { sInstance.closeInternal(); - if (resId != 0) { - sInstance.loadDictionary(context, resId); + try { + final AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId); + if (afd == null) { + Log.e(TAG, "Found the resource but it is compressed. resId=" + resId); + return null; + } + final String sourceDir = context.getApplicationInfo().sourceDir; + final File packagePath = new File(sourceDir); + // TODO: Come up with a way to handle a directory. + if (!packagePath.isFile()) { + Log.e(TAG, "sourceDir is not a file: " + sourceDir); + return null; + } + sInstance.loadDictionary(sourceDir, afd.getStartOffset(), afd.getLength()); sInstance.mDicTypeId = dicTypeId; + } catch (android.content.res.Resources.NotFoundException e) { + Log.e(TAG, "Could not find the resource. resId=" + resId); + return null; + } + } + return sInstance; + } + + // For unit test + /* package */ static BinaryDictionary initDictionary(File dictionary, long startOffset, + long length, int dicTypeId) { + synchronized (sInstance) { + sInstance.closeInternal(); + if (dictionary.isFile()) { + sInstance.loadDictionary(dictionary.getAbsolutePath(), startOffset, length); + sInstance.mDicTypeId = dicTypeId; + } else { + Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath()); + return null; } } return sInstance; @@ -92,22 +124,11 @@ public class BinaryDictionary extends Dictionary { int[] inputCodes, int inputCodesLength, char[] outputChars, int[] frequencies, int maxWordLength, int maxBigrams, int maxAlternatives); - private final void loadDictionary(Context context, int resId) { - try { - final AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId); - if (afd == null) { - Log.e(TAG, "Found the resource but it is compressed. resId=" + resId); - return; - } - mNativeDict = openNative(context.getApplicationInfo().sourceDir, - afd.getStartOffset(), afd.getLength(), + private final void loadDictionary(String path, long startOffset, long length) { + mNativeDict = openNative(path, startOffset, length, TYPED_LETTER_MULTIPLIER, FULL_WORD_FREQ_MULTIPLIER, MAX_WORD_LENGTH, MAX_WORDS, MAX_ALTERNATIVES); - mDictLength = afd.getLength(); - } catch (android.content.res.Resources.NotFoundException e) { - Log.e(TAG, "Could not find the resource. resId=" + resId); - return; - } + mDictLength = length; } @Override diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index fc45c7c75..9699ad136 100644 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -54,7 +54,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); private static final int MAX_SUGGESTIONS = 16; - private static boolean DBG = LatinImeLogger.sDBG; + private static final boolean DBG = LatinImeLogger.sDBG; private final ArrayList<View> mWords = new ArrayList<View>(); private final boolean mConfigCandidateHighlightFontColorEnabled; @@ -226,10 +226,14 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } final String debugString = info.getDebugString(); if (DBG) { - if (!TextUtils.isEmpty(debugString)) { + if (TextUtils.isEmpty(debugString)) { + dv.setVisibility(GONE); + } else { dv.setText(debugString); dv.setVisibility(VISIBLE); } + } else { + dv.setVisibility(GONE); } } else { dv.setVisibility(GONE); @@ -249,8 +253,10 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo final TextView tv = (TextView)mWords.get(1).findViewById(R.id.candidate_word); final Spannable word = new SpannableString(autoCorrectedWord); final int wordLength = word.length(); - word.setSpan(mInvertedBackgroundColorSpan, 0, wordLength, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - word.setSpan(mInvertedForegroundColorSpan, 0, wordLength, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + word.setSpan(mInvertedBackgroundColorSpan, 0, wordLength, + Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + word.setSpan(mInvertedForegroundColorSpan, 0, wordLength, + Spanned.SPAN_INCLUSIVE_EXCLUSIVE); tv.setText(word); mShowingAutoCorrectionInverted = true; } diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java index 03211f36b..2f1e7c2b8 100644 --- a/java/src/com/android/inputmethod/latin/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/DebugSettings.java @@ -20,6 +20,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; +import android.os.Process; import android.preference.CheckBoxPreference; import android.preference.PreferenceActivity; import android.util.Log; @@ -30,6 +31,7 @@ public class DebugSettings extends PreferenceActivity private static final String TAG = "DebugSettings"; private static final String DEBUG_MODE_KEY = "debug_mode"; + private boolean mServiceNeedsRestart = false; private CheckBoxPreference mDebugMode; @Override @@ -39,16 +41,24 @@ public class DebugSettings extends PreferenceActivity SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); prefs.registerOnSharedPreferenceChangeListener(this); + mServiceNeedsRestart = false; mDebugMode = (CheckBoxPreference) findPreference(DEBUG_MODE_KEY); updateDebugMode(); } @Override + protected void onStop() { + super.onStop(); + if (mServiceNeedsRestart) Process.killProcess(Process.myPid()); + } + + @Override public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { if (key.equals(DEBUG_MODE_KEY)) { if (mDebugMode != null) { mDebugMode.setChecked(prefs.getBoolean(DEBUG_MODE_KEY, false)); updateDebugMode(); + mServiceNeedsRestart = true; } } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 5ce1b7e95..67a9f3203 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -359,9 +359,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // 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)); + res.getBoolean(R.bool.config_default_recorrection_enabled)); } else { - mReCorrectionEnabled = res.getBoolean(R.bool.default_recorrection_enabled); + mReCorrectionEnabled = res.getBoolean(R.bool.config_default_recorrection_enabled); } mConfigEnableShowSubtypeSettings = res.getBoolean( @@ -1950,7 +1950,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else if (Settings.PREF_RECORRECTION_ENABLED.equals(key)) { mReCorrectionEnabled = sharedPreferences.getBoolean( Settings.PREF_RECORRECTION_ENABLED, - mResources.getBoolean(R.bool.default_recorrection_enabled)); + mResources.getBoolean(R.bool.config_default_recorrection_enabled)); } } @@ -2082,7 +2082,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void updateAutoTextEnabled() { if (mSuggest == null) return; - mSuggest.setAutoTextEnabled(mQuickFixes + mSuggest.setQuickFixesEnabled(mQuickFixes && SubtypeSwitcher.getInstance().isSystemLanguageSameAsInputLanguage()); } @@ -2121,7 +2121,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); mVibrateOn = vibrator != null && vibrator.hasVibrator() && prefs.getBoolean(Settings.PREF_VIBRATE_ON, false); - mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON, false); + mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON, + mResources.getBoolean(R.bool.config_default_sound_enabled)); mPopupOn = isPopupEnabled(prefs); mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true); @@ -2272,10 +2273,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen di.dismiss(); switch (position) { case 0: - launchSettings(); + mImm.showInputMethodPicker(); break; case 1: - mImm.showInputMethodPicker(); + launchSettings(); break; } } diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 12338ce61..341d5add0 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -225,7 +225,9 @@ public class Settings extends PreferenceActivity final String action; if (android.os.Build.VERSION.SDK_INT >= /* android.os.Build.VERSION_CODES.HONEYCOMB */ 11) { - action = "android.settings.INPUT_METHOD_AND_SUBTYPE_ENABLER"; + // Refer to android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS + // TODO: Can this be a constant instead of literal String constant? + action = "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS"; } else { action = "com.android.inputmethod.latin.INPUT_LANGUAGE_SELECTION"; } diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index f4262cc99..d8c89a0d4 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -426,8 +426,15 @@ public class SubtypeSwitcher { if (mConfigUseSpacebarLanguageSwitcher) { return mLanguageSwitcher.getEnabledLanguages(); } else { + int enabledLanguageCount = mEnabledLanguagesOfCurrentInputMethod.size(); + // Workaround for explicitly specifying the voice language + if (enabledLanguageCount == 1) { + mEnabledLanguagesOfCurrentInputMethod.add( + mEnabledLanguagesOfCurrentInputMethod.get(0)); + ++enabledLanguageCount; + } return mEnabledLanguagesOfCurrentInputMethod.toArray( - new String[mEnabledLanguagesOfCurrentInputMethod.size()]); + new String[enabledLanguageCount]); } } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index ced355bb2..c9e57d0a5 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -22,6 +22,7 @@ import android.text.TextUtils; import android.util.Log; import android.view.View; +import java.io.File; import java.util.ArrayList; import java.util.Arrays; @@ -64,7 +65,7 @@ public class Suggest implements Dictionary.WordCallback { static final int LARGE_DICTIONARY_THRESHOLD = 200 * 1000; - private static boolean DBG = LatinImeLogger.sDBG; + private static final boolean DBG = LatinImeLogger.sDBG; private BinaryDictionary mMainDict; @@ -80,7 +81,7 @@ public class Suggest implements Dictionary.WordCallback { private static final int PREF_MAX_BIGRAMS = 60; - private boolean mAutoTextEnabled; + private boolean mQuickFixesEnabled; private double mAutoCorrectionThreshold; private int[] mPriorities = new int[mPrefMaxSuggestions]; @@ -109,6 +110,12 @@ public class Suggest implements Dictionary.WordCallback { initPool(); } + // For unit test + /* package */ Suggest(File dictionary, long startOffset, long length) { + mMainDict = BinaryDictionary.initDictionary(dictionary, startOffset, length, DIC_MAIN); + initPool(); + } + private void initPool() { for (int i = 0; i < mPrefMaxSuggestions; i++) { StringBuilder sb = new StringBuilder(getApproxMaxWordLength()); @@ -116,8 +123,8 @@ public class Suggest implements Dictionary.WordCallback { } } - public void setAutoTextEnabled(boolean enabled) { - mAutoTextEnabled = enabled; + public void setQuickFixesEnabled(boolean enabled) { + mQuickFixesEnabled = enabled; } public int getCorrectionMode() { @@ -224,6 +231,7 @@ public class Suggest implements Dictionary.WordCallback { mLowerOriginalWord = ""; } + double normalizedScore = Integer.MIN_VALUE; if (wordComposer.size() == 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM || mCorrectionMode == CORRECTION_BASIC)) { // At first character typed, search only the bigrams @@ -290,9 +298,9 @@ public class Suggest implements Dictionary.WordCallback { && mSuggestions.size() > 0 && mPriorities.length > 0) { // TODO: when the normalized score of the first suggestion is nearly equals to // the normalized score of the second suggestion, behave less aggressive. - final double normalizedScore = Utils.calcNormalizedScore( + normalizedScore = Utils.calcNormalizedScore( typedWord, mSuggestions.get(0), mPriorities[0]); - if (LatinImeLogger.sDBG) { + if (DBG) { Log.d(TAG, "Normalized " + typedWord + "," + mSuggestions.get(0) + "," + mPriorities[0] + ", " + normalizedScore + "(" + mAutoCorrectionThreshold + ")"); @@ -308,7 +316,7 @@ public class Suggest implements Dictionary.WordCallback { if (typedWord != null) { mSuggestions.add(0, typedWord.toString()); } - if (mAutoTextEnabled) { + if (mQuickFixesEnabled) { int i = 0; int max = 6; // Don't autotext the suggestions from the dictionaries @@ -354,7 +362,30 @@ public class Suggest implements Dictionary.WordCallback { } } removeDupes(); - return new SuggestedWords.Builder().addWords(mSuggestions, null); + if (DBG) { + ArrayList<SuggestedWords.SuggestedWordInfo> frequencyInfoList = + new ArrayList<SuggestedWords.SuggestedWordInfo>(); + frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false)); + final int priorityLength = mPriorities.length; + for (int i = 0; i < priorityLength; ++i) { + if (normalizedScore > 0) { + final String priorityThreshold = Integer.toString(mPriorities[i]) + " (" + + normalizedScore + ")"; + frequencyInfoList.add( + new SuggestedWords.SuggestedWordInfo(priorityThreshold, false)); + normalizedScore = 0.0; + } else { + final String priority = Integer.toString(mPriorities[i]); + frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo(priority, false)); + } + } + for (int i = priorityLength; i < mSuggestions.size(); ++i) { + frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false)); + } + return new SuggestedWords.Builder().addWords(mSuggestions, frequencyInfoList); + } else { + return new SuggestedWords.Builder().addWords(mSuggestions, null); + } } public int[] getNextLettersFrequencies() { @@ -392,12 +423,12 @@ public class Suggest implements Dictionary.WordCallback { return mHasAutoCorrection; } - private boolean compareCaseInsensitive(final String mLowerOriginalWord, + private static boolean compareCaseInsensitive(final String lowerOriginalWord, final char[] word, final int offset, final int length) { - final int originalLength = mLowerOriginalWord.length(); + final int originalLength = lowerOriginalWord.length(); if (originalLength == length && Character.isUpperCase(word[offset])) { for (int i = 0; i < originalLength; i++) { - if (mLowerOriginalWord.charAt(i) != Character.toLowerCase(word[offset+i])) { + if (lowerOriginalWord.charAt(i) != Character.toLowerCase(word[offset+i])) { return false; } } diff --git a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java index 61a194a8d..277ef7e65 100644 --- a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java +++ b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java @@ -38,16 +38,13 @@ import android.os.IBinder; import android.preference.PreferenceManager; import android.provider.Browser; import android.speech.SpeechRecognizer; -import android.text.Layout; -import android.text.Selection; -import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.Spanned; import android.text.TextUtils; import android.text.method.LinkMovementMethod; -import android.text.style.ClickableSpan; import android.text.style.URLSpan; import android.util.Log; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; @@ -83,9 +80,8 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { private static final String IME_OPTION_NO_MICROPHONE = "nm"; private static final int RECOGNITIONVIEW_HEIGHT_THRESHOLD_RATIO = 6; - @SuppressWarnings("unused") - private static final String TAG = "VoiceIMEConnector"; - private static boolean DEBUG = LatinImeLogger.sDBG; + private static final String TAG = VoiceIMEConnector.class.getSimpleName(); + private static final boolean DEBUG = LatinImeLogger.sDBG; private boolean mAfterVoiceInput; private boolean mHasUsedVoiceInput; @@ -177,7 +173,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { if (mVoiceWarningDialog != null && mVoiceWarningDialog.isShowing()) { return; } - AlertDialog.Builder builder = new AlertDialog.Builder(mService); + AlertDialog.Builder builder = new UrlLinkAlertDialogBuilder(mService); builder.setCancelable(true); builder.setIcon(R.drawable.ic_mic_dialog); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @@ -215,90 +211,80 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { mService.getText(R.string.voice_warning_how_to_turn_off)); } builder.setMessage(message); - builder.setTitle(R.string.voice_warning_title); mVoiceWarningDialog = builder.create(); - Window window = mVoiceWarningDialog.getWindow(); - WindowManager.LayoutParams lp = window.getAttributes(); + final Window window = mVoiceWarningDialog.getWindow(); + final WindowManager.LayoutParams lp = window.getAttributes(); lp.token = token; lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; window.setAttributes(lp); window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); mVoiceInput.logKeyboardWarningDialogShown(); mVoiceWarningDialog.show(); - // Make URL in the dialog message clickable - TextView textView = (TextView) mVoiceWarningDialog.findViewById(android.R.id.message); - if (textView != null) { - final CustomLinkMovementMethod method = CustomLinkMovementMethod.getInstance(); - method.setVoiceWarningDialog(mVoiceWarningDialog); - textView.setMovementMethod(method); - } } - private static class CustomLinkMovementMethod extends LinkMovementMethod { - private static CustomLinkMovementMethod sLinkMovementMethodInstance = - new CustomLinkMovementMethod(); + private static class UrlLinkAlertDialogBuilder extends AlertDialog.Builder { private AlertDialog mAlertDialog; - public void setVoiceWarningDialog(AlertDialog alertDialog) { - mAlertDialog = alertDialog; + public UrlLinkAlertDialogBuilder(Context context) { + super(context); } - public static CustomLinkMovementMethod getInstance() { - return sLinkMovementMethodInstance; + @Override + public AlertDialog.Builder setMessage(CharSequence message) { + return super.setMessage(replaceURLSpan(message)); + } + + private Spanned replaceURLSpan(CharSequence message) { + // Replace all spans with the custom span + final SpannableStringBuilder ssb = new SpannableStringBuilder(message); + for (URLSpan span : ssb.getSpans(0, ssb.length(), URLSpan.class)) { + int spanStart = ssb.getSpanStart(span); + int spanEnd = ssb.getSpanEnd(span); + int spanFlags = ssb.getSpanFlags(span); + ssb.removeSpan(span); + ssb.setSpan(new ClickableSpan(span.getURL()), spanStart, spanEnd, spanFlags); + } + return ssb; } - // Almost the same as LinkMovementMethod.onTouchEvent(), but overrides it for - // FLAG_ACTIVITY_NEW_TASK and mAlertDialog.cancel(). @Override - public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { - int action = event.getAction(); - - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { - int x = (int) event.getX(); - int y = (int) event.getY(); - - x -= widget.getTotalPaddingLeft(); - y -= widget.getTotalPaddingTop(); - - x += widget.getScrollX(); - y += widget.getScrollY(); - - Layout layout = widget.getLayout(); - int line = layout.getLineForVertical(y); - int off = layout.getOffsetForHorizontal(line, x); - - ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); - - if (link.length != 0) { - if (action == MotionEvent.ACTION_UP) { - if (link[0] instanceof URLSpan) { - URLSpan urlSpan = (URLSpan) link[0]; - Uri uri = Uri.parse(urlSpan.getURL()); - Context context = widget.getContext(); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); - if (mAlertDialog != null) { - // Go back to the previous IME for now. - // TODO: If we can find a way to bring the new activity to front - // while keeping the warning dialog, we don't need to cancel here. - mAlertDialog.cancel(); - } - context.startActivity(intent); - } else { - link[0].onClick(widget); - } - } else if (action == MotionEvent.ACTION_DOWN) { - Selection.setSelection(buffer, buffer.getSpanStart(link[0]), - buffer.getSpanEnd(link[0])); + public AlertDialog create() { + final AlertDialog dialog = super.create(); + + dialog.setOnShowListener(new DialogInterface.OnShowListener() { + @Override + public void onShow(DialogInterface dialogInterface) { + // Make URL in the dialog message click-able. + TextView textView = (TextView) mAlertDialog.findViewById(android.R.id.message); + if (textView != null) { + textView.setMovementMethod(LinkMovementMethod.getInstance()); } - return true; - } else { - Selection.removeSelection(buffer); } + }); + mAlertDialog = dialog; + return dialog; + } + + class ClickableSpan extends URLSpan { + public ClickableSpan(String url) { + super(url); + } + + @Override + public void onClick(View widget) { + Uri uri = Uri.parse(getURL()); + Context context = widget.getContext(); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + // Add this flag to start an activity from service + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); + // Dismiss the warning dialog and go back to the previous IME. + // TODO: If we can find a way to bring the new activity to front while keeping + // the warning dialog, we don't need to dismiss it here. + mAlertDialog.cancel(); + context.startActivity(intent); } - return super.onTouchEvent(widget, buffer, event); } } @@ -729,7 +715,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { mHandler.updateVoiceResults(); } - public FieldContext makeFieldContext() { + private FieldContext makeFieldContext() { SubtypeSwitcher switcher = SubtypeSwitcher.getInstance(); return new FieldContext(mService.getCurrentInputConnection(), mService.getCurrentInputEditorInfo(), switcher.getInputLocaleStr(), |