diff options
Diffstat (limited to 'java/src')
28 files changed, 566 insertions, 422 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java index 1836f27b3..9caed00c9 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java @@ -17,7 +17,6 @@ package com.android.inputmethod.accessibility; import android.content.Context; -import android.content.SharedPreferences; import android.inputmethodservice.InputMethodService; import android.media.AudioManager; import android.os.SystemClock; @@ -55,15 +54,15 @@ public class AccessibilityUtils { */ private static final boolean ENABLE_ACCESSIBILITY = true; - public static void init(InputMethodService inputMethod, SharedPreferences prefs) { + public static void init(InputMethodService inputMethod) { if (!ENABLE_ACCESSIBILITY) return; // These only need to be initialized if the kill switch is off. - sInstance.initInternal(inputMethod, prefs); - KeyCodeDescriptionMapper.init(inputMethod, prefs); - AccessibleInputMethodServiceProxy.init(inputMethod, prefs); - AccessibleKeyboardViewProxy.init(inputMethod, prefs); + sInstance.initInternal(inputMethod); + KeyCodeDescriptionMapper.init(); + AccessibleInputMethodServiceProxy.init(inputMethod); + AccessibleKeyboardViewProxy.init(inputMethod); } public static AccessibilityUtils getInstance() { @@ -74,7 +73,7 @@ public class AccessibilityUtils { // This class is not publicly instantiable. } - private void initInternal(Context context, SharedPreferences prefs) { + private void initInternal(Context context) { mContext = context; mAccessibilityManager = (AccessibilityManager) context .getSystemService(Context.ACCESSIBILITY_SERVICE); diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java index 4ab9cb898..d834dd10b 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java @@ -17,7 +17,6 @@ package com.android.inputmethod.accessibility; import android.content.Context; -import android.content.SharedPreferences; import android.inputmethodservice.InputMethodService; import android.media.AudioManager; import android.os.Looper; @@ -82,8 +81,8 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi } } - public static void init(InputMethodService inputMethod, SharedPreferences prefs) { - sInstance.initInternal(inputMethod, prefs); + public static void init(InputMethodService inputMethod) { + sInstance.initInternal(inputMethod); } public static AccessibleInputMethodServiceProxy getInstance() { @@ -94,7 +93,7 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi // Not publicly instantiable. } - private void initInternal(InputMethodService inputMethod, SharedPreferences prefs) { + private void initInternal(InputMethodService inputMethod) { mInputMethod = inputMethod; mVibrator = (Vibrator) inputMethod.getSystemService(Context.VIBRATOR_SERVICE); mAudioManager = (AudioManager) inputMethod.getSystemService(Context.AUDIO_SERVICE); @@ -125,8 +124,6 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi */ @Override public void onFlickGesture(int direction) { - final int keyEventCode; - switch (direction) { case FlickGestureDetector.FLICK_LEFT: sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT); diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index 4cb2f20b9..9141daaee 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -17,7 +17,6 @@ package com.android.inputmethod.accessibility; import android.content.Context; -import android.content.SharedPreferences; import android.graphics.Color; import android.graphics.Paint; import android.inputmethodservice.InputMethodService; @@ -43,8 +42,8 @@ public class AccessibleKeyboardViewProxy { private Key mLastHoverKey = null; - public static void init(InputMethodService inputMethod, SharedPreferences prefs) { - sInstance.initInternal(inputMethod, prefs); + public static void init(InputMethodService inputMethod) { + sInstance.initInternal(inputMethod); sInstance.mListener = AccessibleInputMethodServiceProxy.getInstance(); } @@ -60,7 +59,7 @@ public class AccessibleKeyboardViewProxy { // Not publicly instantiable. } - private void initInternal(InputMethodService inputMethod, SharedPreferences prefs) { + private void initInternal(InputMethodService inputMethod) { final Paint paint = new Paint(); paint.setTextAlign(Paint.Align.LEFT); paint.setTextSize(14.0f); @@ -71,8 +70,7 @@ public class AccessibleKeyboardViewProxy { mGestureDetector = new KeyboardFlickGestureDetector(inputMethod); } - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event, - PointerTracker tracker) { + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { if (mView == null) { Log.e(TAG, "No keyboard view set!"); return false; @@ -132,9 +130,9 @@ public class AccessibleKeyboardViewProxy { final Key key = tracker.getKeyOn(x, y); if (key != mLastHoverKey) { - fireKeyHoverEvent(tracker, mLastHoverKey, false); + fireKeyHoverEvent(mLastHoverKey, false); mLastHoverKey = key; - fireKeyHoverEvent(tracker, mLastHoverKey, true); + fireKeyHoverEvent(mLastHoverKey, true); } return true; @@ -143,7 +141,7 @@ public class AccessibleKeyboardViewProxy { return false; } - private void fireKeyHoverEvent(PointerTracker tracker, Key key, boolean entering) { + private void fireKeyHoverEvent(Key key, boolean entering) { if (mListener == null) { Log.e(TAG, "No accessible keyboard action listener set!"); return; diff --git a/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java b/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java index 9d99e3131..db12f76ad 100644 --- a/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java +++ b/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java @@ -126,7 +126,6 @@ public abstract class FlickGestureDetector { } final float distanceSquare = calculateDistanceSquare(mCachedHoverEnter, event); - final long timeout = event.getEventTime() - mCachedHoverEnter.getEventTime(); switch (event.getAction()) { case MotionEventCompatUtils.ACTION_HOVER_MOVE: diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index e01262c20..3d5ab05c3 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -17,7 +17,6 @@ package com.android.inputmethod.accessibility; import android.content.Context; -import android.content.SharedPreferences; import android.text.TextUtils; import com.android.inputmethod.keyboard.Key; @@ -45,8 +44,8 @@ public class KeyCodeDescriptionMapper { // Map of shift-locked key codes to spoken description resource IDs private final HashMap<Integer, Integer> mShiftLockedKeyCodeMap; - public static void init(Context context, SharedPreferences prefs) { - sInstance.initInternal(context, prefs); + public static void init() { + sInstance.initInternal(); } public static KeyCodeDescriptionMapper getInstance() { @@ -60,7 +59,7 @@ public class KeyCodeDescriptionMapper { mShiftLockedKeyCodeMap = new HashMap<Integer, Integer>(); } - private void initInternal(Context context, SharedPreferences prefs) { + private void initInternal() { // Manual label substitutions for key labels with no string resource mKeyLabelMap.put(":-)", R.string.spoken_description_smiley); diff --git a/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java b/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java index f6afbcfe2..011473bef 100644 --- a/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/ArraysCompatUtils.java @@ -16,10 +16,14 @@ package com.android.inputmethod.compat; +import android.util.Log; + import java.lang.reflect.Method; import java.util.Arrays; public class ArraysCompatUtils { + private static final String TAG = ArraysCompatUtils.class.getSimpleName(); + private static final Method METHOD_Arrays_binarySearch = CompatUtils .getMethod(Arrays.class, "binarySearch", int[].class, int.class, int.class, int.class); @@ -33,8 +37,15 @@ public class ArraysCompatUtils { } } - /* package */ static int compatBinarySearch(int[] array, int startIndex, int endIndex, - int value) { + // TODO: Implement fast binary search + /* package for testing */ + static int compatBinarySearch(int[] array, int startIndex, int endIndex, int value) { + // Output error log because this method has strict performance penalty. + // Note that this method has been called only from spell checker and spell checker exists + // only from IceCreamSandwich and after, so that there is no chance on pre-ICS device to + // invoke this method. + Log.e(TAG, "Invoked expensive binarySearch"); + if (startIndex > endIndex) throw new IllegalArgumentException(); if (startIndex < 0 || endIndex > array.length) throw new ArrayIndexOutOfBoundsException(); diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 4e4ccef18..42d1fe1ac 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -73,10 +73,11 @@ public class Key { private static final int LABEL_FLAGS_WITH_ICON_RIGHT = 0x2000; private static final int LABEL_FLAGS_AUTO_X_SCALE = 0x4000; + // TODO: This should be public final /** Icon to display instead of a label. Icon takes precedence over a label */ private Drawable mIcon; /** Preview version of the icon, for the preview popup */ - private Drawable mPreviewIcon; + public final Drawable mPreviewIcon; /** Width of the key, not including the gap */ public final int mWidth; @@ -198,6 +199,7 @@ public class Key { mCode = code; mAltCode = Keyboard.CODE_DUMMY; mIcon = icon; + mPreviewIcon = null; // Horizontal gap is divided equally to both sides of the key. mX = x + mHorizontalGap / 2; mY = y; @@ -425,18 +427,11 @@ public class Key { return mIcon; } - public Drawable getPreviewIcon() { - return mPreviewIcon; - } - + // TODO: Get rid of this method. public void setIcon(Drawable icon) { mIcon = icon; } - public void setPreviewIcon(Drawable icon) { - mPreviewIcon = icon; - } - /** * Informs the key that it has been pressed, in case it needs to change its appearance or * state. diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 3b3ff0709..b4172d41e 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -73,15 +73,6 @@ public class KeyboardId { private final int mHashCode; public KeyboardId(int xmlId, int elementState, Locale locale, int orientation, int width, - int mode, EditorInfo editorInfo, boolean settingsKeyEnabled, - boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) { - this(xmlId, elementState, locale, orientation, width, mode, - (editorInfo != null ? editorInfo.inputType : 0), - (editorInfo != null ? editorInfo.imeOptions : 0), - settingsKeyEnabled, clobberSettingsKey, shortcutKeyEnabled, hasShortcutKey); - } - - private KeyboardId(int xmlId, int elementState, Locale locale, int orientation, int width, int mode, int inputType, int imeOptions, boolean settingsKeyEnabled, boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) { this.mLocale = locale; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java index a28cfa85d..34296fa2a 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java @@ -17,11 +17,9 @@ package com.android.inputmethod.keyboard; import android.content.Context; -import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; -import android.util.DisplayMetrics; import android.util.Xml; import android.view.inputmethod.EditorInfo; @@ -42,9 +40,9 @@ import java.util.Locale; /** * This class has a set of {@link KeyboardId}s. Each of them represents a different keyboard - * specific to a keyboard state, such as alphabet, symbols, and so on. Layouts in the same - * {@link KeyboardSet} are related to each other. - * A {@link KeyboardSet} needs to be created for each {@link android.view.inputmethod.EditorInfo}. + * specific to a keyboard state, such as alphabet, symbols, and so on. Layouts in the same + * {@link KeyboardSet} are related to each other. A {@link KeyboardSet} needs to be created for each + * {@link android.view.inputmethod.EditorInfo}. */ public class KeyboardSet { private static final String TAG_KEYBOARD_SET = "KeyboardSet"; @@ -55,56 +53,67 @@ public class KeyboardSet { public final KeyboardId mSymbolsId; public final KeyboardId mSymbolsShiftedId; - KeyboardSet(Builder builder) { - mAlphabetId = builder.getKeyboardId(false, false); - mSymbolsId = builder.getKeyboardId(true, false); - mSymbolsShiftedId = builder.getKeyboardId(true, true); + KeyboardSet(Params params) { + mAlphabetId = Builder.getKeyboardId(false, false, params); + mSymbolsId = Builder.getKeyboardId(true, false, params); + mSymbolsShiftedId = Builder.getKeyboardId(true, true, params); + } + + private static class Params { + int mMode; + int mInputTypes; + int mImeOptions; + boolean mSettingsKeyEnabled; + boolean mVoiceKeyEnabled; + boolean mVoiceKeyOnMain; + boolean mNoSettingsKey; + Locale mLocale; + int mOrientation; + int mWidth; + final HashMap<Integer, Integer> mElementKeyboards = + new HashMap<Integer, Integer>(); + + Params() {} } public static class Builder { private final Resources mResources; - private final EditorInfo mEditorInfo; - private final HashMap<Integer, Integer> mElementKeyboards = - new HashMap<Integer, Integer>(); - - private final int mMode; - private final boolean mSettingsKeyEnabled; - private final boolean mVoiceKeyEnabled; - private final boolean mVoiceKeyOnMain; - private final boolean mNoSettingsKey; - private final Locale mLocale; - private final Configuration mConf; - private final DisplayMetrics mMetrics; + private final Params mParams = new Params(); public Builder(Context context, EditorInfo editorInfo, SettingsValues settingsValues) { mResources = context.getResources(); - mEditorInfo = editorInfo; final SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance(); final String packageName = context.getPackageName(); + final Params params = mParams; - mMode = Utils.getKeyboardMode(mEditorInfo); - mSettingsKeyEnabled = settingsValues.isSettingsKeyEnabled(); + params.mMode = Utils.getKeyboardMode(editorInfo); + if (editorInfo != null) { + params.mInputTypes = editorInfo.inputType; + params.mImeOptions = editorInfo.imeOptions; + } + params.mSettingsKeyEnabled = settingsValues.isSettingsKeyEnabled(); @SuppressWarnings("deprecation") final boolean noMicrophone = Utils.inPrivateImeOptions( packageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo) || Utils.inPrivateImeOptions( null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo); - mVoiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo) && !noMicrophone; - mVoiceKeyOnMain = settingsValues.isVoiceKeyOnMain(); - mNoSettingsKey = Utils.inPrivateImeOptions( + params.mVoiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo) && !noMicrophone; + params.mVoiceKeyOnMain = settingsValues.isVoiceKeyOnMain(); + params.mNoSettingsKey = Utils.inPrivateImeOptions( packageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo); final boolean forceAscii = Utils.inPrivateImeOptions( packageName, LatinIME.IME_OPTION_FORCE_ASCII, editorInfo); final boolean asciiCapable = subtypeSwitcher.currentSubtypeContainsExtraValueKey( LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE); - mLocale = (forceAscii && !asciiCapable) ? Locale.US : subtypeSwitcher.getInputLocale(); - mConf = mResources.getConfiguration(); - mMetrics = mResources.getDisplayMetrics(); + params.mLocale = (forceAscii && !asciiCapable) + ? Locale.US : subtypeSwitcher.getInputLocale(); + params.mOrientation = mResources.getConfiguration().orientation; + params.mWidth = mResources.getDisplayMetrics().widthPixels; } public KeyboardSet build() { - final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, mLocale); + final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, mParams.mLocale); try { parseKeyboardSet(mResources, R.xml.keyboard_set); } catch (Exception e) { @@ -112,16 +121,18 @@ public class KeyboardSet { } finally { LocaleUtils.setSystemLocale(mResources, savedLocale); } - return new KeyboardSet(this); + return new KeyboardSet(mParams); } - KeyboardId getKeyboardId(boolean isSymbols, boolean isShift) { - final int elementState = getElementState(mMode, isSymbols, isShift); - final int xmlId = mElementKeyboards.get(elementState); - final boolean hasShortcutKey = mVoiceKeyEnabled && (isSymbols != mVoiceKeyOnMain); - return new KeyboardId(xmlId, elementState, mLocale, mConf.orientation, - mMetrics.widthPixels, mMode, mEditorInfo, mSettingsKeyEnabled, mNoSettingsKey, - mVoiceKeyEnabled, hasShortcutKey); + static KeyboardId getKeyboardId(boolean isSymbols, boolean isShift, Params params) { + final int elementState = getElementState(params.mMode, isSymbols, isShift); + final int xmlId = params.mElementKeyboards.get(elementState); + final boolean hasShortcutKey = params.mVoiceKeyEnabled + && (isSymbols != params.mVoiceKeyOnMain); + return new KeyboardId(xmlId, elementState, params.mLocale, params.mOrientation, + params.mWidth, params.mMode, params.mInputTypes, params.mImeOptions, + params.mSettingsKeyEnabled, params.mNoSettingsKey, params.mVoiceKeyEnabled, + hasShortcutKey); } private static int getElementState(int mode, boolean isSymbols, boolean isShift) { @@ -198,7 +209,7 @@ public class KeyboardSet { R.styleable.KeyboardSet_Element_elementName, 0); final int elementKeyboard = a.getResourceId( R.styleable.KeyboardSet_Element_elementKeyboard, 0); - mElementKeyboards.put(elementName, elementKeyboard); + mParams.mElementKeyboards.put(elementName, elementKeyboard); } finally { a.recycle(); } @@ -208,7 +219,8 @@ public class KeyboardSet { public static String parseKeyboardLocale(Resources res, int resId) throws XmlPullParserException, IOException { final XmlPullParser parser = res.getXml(resId); - if (parser == null) return ""; + if (parser == null) + return ""; int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { if (event == XmlPullParser.START_TAG) { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 1f3006d9f..3d4a6efcd 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -166,18 +166,29 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, SettingsValues.getKeyPreviewPopupDismissDelay(mPrefs, mResources)); final boolean localeChanged = (oldKeyboard == null) || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); - mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged); + if (keyboard instanceof LatinKeyboard) { + final LatinKeyboard latinKeyboard = (LatinKeyboard)keyboard; + latinKeyboard.updateAutoCorrectionState(mIsAutoCorrectionActive); + // 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. It is also applicable to shortcut key. + latinKeyboard.updateSpacebarLanguage(0.0f, + Utils.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */), + mSubtypeSwitcher.needsToDisplayLanguage(latinKeyboard.mId.mLocale)); + latinKeyboard.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); + } updateShiftState(); + mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged); } // TODO: Move this method to KeyboardSet. - private LatinKeyboard getKeyboard(KeyboardId id) { + private LatinKeyboard getKeyboard(Context context, KeyboardId id) { final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id); LatinKeyboard keyboard = (ref == null) ? null : ref.get(); if (keyboard == null) { final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, id.mLocale); try { - final LatinKeyboard.Builder builder = new LatinKeyboard.Builder(mThemeContext); + final LatinKeyboard.Builder builder = new LatinKeyboard.Builder(context); builder.load(id); builder.setTouchPositionCorrectionEnabled( mSubtypeSwitcher.currentSubtypeContainsExtraValueKey( @@ -198,14 +209,8 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, + " theme=" + themeName(keyboard.mThemeId)); } - keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive); keyboard.setShiftLocked(false); 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. It is also applicable to shortcut key. - keyboard.setSpacebarTextFadeFactor(0.0f, null); - keyboard.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady(), null); return keyboard; } @@ -338,19 +343,19 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, // Implements {@link KeyboardState.SwitchActions}. @Override public void setSymbolsKeyboard() { - setKeyboard(getKeyboard(mKeyboardSet.mSymbolsId)); + setKeyboard(getKeyboard(mThemeContext, mKeyboardSet.mSymbolsId)); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setAlphabetKeyboard() { - setKeyboard(getKeyboard(mKeyboardSet.mAlphabetId)); + setKeyboard(getKeyboard(mThemeContext, mKeyboardSet.mAlphabetId)); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setSymbolsShiftedKeyboard() { - final Keyboard keyboard = getKeyboard(mKeyboardSet.mSymbolsShiftedId); + final Keyboard keyboard = getKeyboard(mThemeContext, mKeyboardSet.mSymbolsShiftedId); setKeyboard(keyboard); // TODO: Remove this logic once we introduce initial keyboard shift state attribute. // Symbol shift keyboard may have a shift key that has a caps lock style indicator (a.k.a. @@ -451,12 +456,22 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, } } + public void onNetworkStateChanged() { + final LatinKeyboard keyboard = getLatinKeyboard(); + if (keyboard == null) return; + final Key updatedKey = keyboard.updateShortcutKey( + SubtypeSwitcher.getInstance().isShortcutImeReady()); + if (updatedKey != null && mKeyboardView != null) { + mKeyboardView.invalidateKey(updatedKey); + } + } + public void onAutoCorrectionStateChanged(boolean isAutoCorrection) { if (mIsAutoCorrectionActive != isAutoCorrection) { mIsAutoCorrectionActive = isAutoCorrection; final LatinKeyboard keyboard = getLatinKeyboard(); if (keyboard != null && keyboard.needsAutoCorrectionSpacebarLed()) { - final Key invalidatedKey = keyboard.onAutoCorrectionStateChanged(isAutoCorrection); + final Key invalidatedKey = keyboard.updateAutoCorrectionState(isAutoCorrection); final LatinKeyboardView keyboardView = getKeyboardView(); if (keyboardView != null) keyboardView.invalidateKey(invalidatedKey); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index d2741edec..ebc0d82b2 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -889,7 +889,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } previewText.setText(mKeyboard.adjustLabelCase(key.mLabel)); } else { - final Drawable previewIcon = key.getPreviewIcon(); + final Drawable previewIcon = key.mPreviewIcon; previewText.setCompoundDrawables(null, null, null, previewIcon != null ? previewIcon : key.getIcon()); previewText.setText(null); diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java index a9fcd9ac4..abb96f0bb 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java @@ -31,11 +31,9 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.text.TextUtils; -import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.Utils; import java.util.Arrays; @@ -48,7 +46,6 @@ public class LatinKeyboard extends Keyboard { private final Resources mRes; private final Theme mTheme; - private final SubtypeSwitcher mSubtypeSwitcher = SubtypeSwitcher.getInstance(); /* Space key and its icons, drawables and colors. */ private final Key mSpaceKey; @@ -57,11 +54,15 @@ public class LatinKeyboard extends Keyboard { private final Drawable mAutoCorrectionSpacebarLedIcon; private final int mSpacebarTextColor; private final int mSpacebarTextShadowColor; - private float mSpacebarTextFadeFactor = 0.0f; private final HashMap<Integer, BitmapDrawable> mSpaceDrawableCache = new HashMap<Integer, BitmapDrawable>(); private final boolean mIsSpacebarTriggeringPopupByLongPress; + private boolean mAutoCorrectionSpacebarLedOn; + private boolean mMultipleEnabledIMEsOrSubtypes; + private boolean mNeedsToDisplayLanguage; + private float mSpacebarTextFadeFactor = 0.0f; + /* Shortcut key and its icons if available */ private final Key mShortcutKey; private final Drawable mEnabledShortcutIcon; @@ -140,11 +141,13 @@ public class LatinKeyboard extends Keyboard { } } - public void setSpacebarTextFadeFactor(float fadeFactor, KeyboardView view) { + public Key updateSpacebarLanguage(float fadeFactor, boolean multipleEnabledIMEsOrSubtypes, + boolean needsToDisplayLanguage) { mSpacebarTextFadeFactor = fadeFactor; - updateSpacebarForLocale(false); - if (view != null) - view.invalidateKey(mSpaceKey); + mMultipleEnabledIMEsOrSubtypes = multipleEnabledIMEsOrSubtypes; + mNeedsToDisplayLanguage = needsToDisplayLanguage; + updateSpacebarIcon(); + return mSpaceKey; } private static int getSpacebarTextColor(int color, float fadeFactor) { @@ -153,13 +156,12 @@ public class LatinKeyboard extends Keyboard { return newColor; } - public void updateShortcutKey(boolean available, KeyboardView view) { + public Key updateShortcutKey(boolean available) { if (mShortcutKey == null) - return; + return null; mShortcutKey.setEnabled(available); mShortcutKey.setIcon(available ? mEnabledShortcutIcon : mDisabledShortcutIcon); - if (view != null) - view.invalidateKey(mShortcutKey); + return mShortcutKey; } public boolean needsAutoCorrectionSpacebarLed() { @@ -169,8 +171,9 @@ public class LatinKeyboard extends Keyboard { /** * @return a key which should be invalidated. */ - public Key onAutoCorrectionStateChanged(boolean isAutoCorrection) { - updateSpacebarForLocale(isAutoCorrection); + public Key updateAutoCorrectionState(boolean isAutoCorrection) { + mAutoCorrectionSpacebarLedOn = isAutoCorrection; + updateSpacebarIcon(); return mSpaceKey; } @@ -183,19 +186,15 @@ public class LatinKeyboard extends Keyboard { return label; } - private void updateSpacebarForLocale(boolean isAutoCorrection) { + private void updateSpacebarIcon() { if (mSpaceKey == null) return; - final InputMethodManagerCompatWrapper imm = InputMethodManagerCompatWrapper.getInstance(); - if (imm == null) return; - // The "..." popup hint for triggering something by a long-pressing the spacebar final boolean shouldShowInputMethodPicker = mIsSpacebarTriggeringPopupByLongPress - && Utils.hasMultipleEnabledIMEsOrSubtypes(imm, true /* include aux subtypes */); + && mMultipleEnabledIMEsOrSubtypes; mSpaceKey.setNeedsSpecialPopupHint(shouldShowInputMethodPicker); - // If application locales are explicitly selected. - if (mSubtypeSwitcher.needsToDisplayLanguage(mId.mLocale)) { - mSpaceKey.setIcon(getSpaceDrawable(mId.mLocale, isAutoCorrection)); - } else if (isAutoCorrection) { - mSpaceKey.setIcon(getSpaceDrawable(null, true)); + if (mNeedsToDisplayLanguage) { + mSpaceKey.setIcon(getSpaceDrawable(mId.mLocale)); + } else if (mAutoCorrectionSpacebarLedOn) { + mSpaceKey.setIcon(getSpaceDrawable(null)); } else { mSpaceKey.setIcon(mSpaceIcon); } @@ -245,15 +244,15 @@ public class LatinKeyboard extends Keyboard { return language; } - private BitmapDrawable getSpaceDrawable(Locale locale, boolean isAutoCorrection) { + private BitmapDrawable getSpaceDrawable(Locale locale) { final Integer hashCode = Arrays.hashCode( - new Object[] { locale, isAutoCorrection, mSpacebarTextFadeFactor }); + new Object[] { locale, mAutoCorrectionSpacebarLedOn, mSpacebarTextFadeFactor }); final BitmapDrawable cached = mSpaceDrawableCache.get(hashCode); if (cached != null) { return cached; } final BitmapDrawable drawable = new BitmapDrawable(mRes, drawSpacebar( - locale, isAutoCorrection, mSpacebarTextFadeFactor)); + locale, mAutoCorrectionSpacebarLedOn, mSpacebarTextFadeFactor)); mSpaceDrawableCache.put(hashCode, drawable); return drawable; } diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index e56f2ea84..c09541f6d 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -21,6 +21,7 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Canvas; import android.os.Message; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; @@ -38,9 +39,11 @@ import com.android.inputmethod.deprecated.VoiceProxy; import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; import com.android.inputmethod.latin.LatinIME; +import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.Utils; +import com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils; import java.util.WeakHashMap; @@ -62,6 +65,9 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke // Timing constants private final int mKeyRepeatInterval; + // TODO: Kill process when the usability study mode was changed. + private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy; + // Mini keyboard private PopupWindow mMoreKeysWindow; private MoreKeysPanel mMoreKeysPanel; @@ -206,7 +212,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke // Detected a double tap on shift key. If we are in the ignoring double tap // mode, it means we have already turned off caps lock in // {@link KeyboardSwitcher#onReleaseShift} . - onDoubleTapShiftKey(tracker, mKeyTimerHandler.isIgnoringDoubleTap()); + onDoubleTapShiftKey(mKeyTimerHandler.isIgnoringDoubleTap()); return true; } // Otherwise these events should not be handled as double tap. @@ -342,8 +348,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke return onLongPress(parentKey, tracker); } - private void onDoubleTapShiftKey(@SuppressWarnings("unused") PointerTracker tracker, - final boolean ignore) { + private void onDoubleTapShiftKey(final boolean ignore) { // When shift key is double tapped, the first tap is correctly processed as usual tap. And // the second tap is treated as this double tap event, so that we need not mark tracker // calling setAlreadyProcessed() nor remove the tracker from mPointerQueue. @@ -374,15 +379,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke return miniKeyboardView; } - public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboard oldKeyboard) { - final Keyboard keyboard = getKeyboard(); - // We should not set text fade factor to the keyboard which does not display the language on - // its spacebar. - if (keyboard instanceof LatinKeyboard && keyboard == oldKeyboard) { - ((LatinKeyboard)keyboard).setSpacebarTextFadeFactor(fadeFactor, this); - } - } - /** * Called when a key is long pressed. By default this will open mini keyboard associated * with this key. @@ -523,6 +519,30 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke x = (int)me.getX(index); y = (int)me.getY(index); } + if (ENABLE_USABILITY_STUDY_LOG) { + final String eventTag; + switch (action) { + case MotionEvent.ACTION_UP: + eventTag = "[Up]"; + break; + case MotionEvent.ACTION_DOWN: + eventTag = "[Down]"; + break; + case MotionEvent.ACTION_POINTER_UP: + eventTag = "[PointerUp]"; + break; + case MotionEvent.ACTION_POINTER_DOWN: + eventTag = "[PointerDown]"; + break; + default: + eventTag = "[Action" + action + "]"; + break; + } + if (!TextUtils.isEmpty(eventTag)) { + UsabilityStudyLogUtils.getInstance().write( + eventTag + eventTime + "," + id + "," + x + "," + y + "\t\t"); + } + } if (mKeyTimerHandler.isInKeyRepeat()) { final PointerTracker tracker = getPointerTracker(id); @@ -579,6 +599,10 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke py = (int)me.getY(i); } tracker.onMoveEvent(px, py, eventTime); + if (ENABLE_USABILITY_STUDY_LOG) { + UsabilityStudyLogUtils.getInstance().write("[Move]" + eventTime + "," + + me.getPointerId(i) + "," + px + "," + py + "\t\t"); + } } } else { getPointerTracker(id).processMotionEvent(action, x, y, eventTime, this); @@ -633,9 +657,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { - final PointerTracker tracker = getPointerTracker(0); return AccessibleKeyboardViewProxy.getInstance().dispatchPopulateAccessibilityEvent( - event, tracker) || super.dispatchPopulateAccessibilityEvent(event); + event) || super.dispatchPopulateAccessibilityEvent(event); } return super.dispatchPopulateAccessibilityEvent(event); diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index 778aac3de..1a636dc2e 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -104,6 +104,20 @@ public class ProximityInfo { final int[] keyWidths = new int[keyCount]; final int[] keyHeights = new int[keyCount]; final int[] keyCharCodes = new int[keyCount]; + final float[] sweetSpotCenterXs; + final float[] sweetSpotCenterYs; + final float[] sweetSpotRadii; + final boolean calculateSweetSpotParams; + if (touchPositionCorrection != null && touchPositionCorrection.isValid()) { + sweetSpotCenterXs = new float[keyCount]; + sweetSpotCenterYs = new float[keyCount]; + sweetSpotRadii = new float[keyCount]; + calculateSweetSpotParams = true; + } else { + sweetSpotCenterXs = sweetSpotCenterYs = sweetSpotRadii = null; + calculateSweetSpotParams = false; + } + for (int i = 0; i < keyCount; ++i) { final Key key = keys.get(i); keyXCoordinates[i] = key.mX; @@ -111,18 +125,23 @@ public class ProximityInfo { keyWidths[i] = key.mWidth; keyHeights[i] = key.mHeight; keyCharCodes[i] = key.mCode; - } - - float[] sweetSpotCenterXs = null; - float[] sweetSpotCenterYs = null; - float[] sweetSpotRadii = null; - - if (touchPositionCorrection != null && touchPositionCorrection.isValid()) { - sweetSpotCenterXs = new float[keyCount]; - sweetSpotCenterYs = new float[keyCount]; - sweetSpotRadii = new float[keyCount]; - calculateSweetSpot(keys, touchPositionCorrection, - sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii); + if (calculateSweetSpotParams) { + final Rect hitBox = key.mHitBox; + final int row = hitBox.top / mKeyHeight; + if (row < touchPositionCorrection.mRadii.length) { + final float hitBoxCenterX = (hitBox.left + hitBox.right) * 0.5f; + final float hitBoxCenterY = (hitBox.top + hitBox.bottom) * 0.5f; + final float hitBoxWidth = hitBox.right - hitBox.left; + final float hitBoxHeight = hitBox.bottom - hitBox.top; + final float x = touchPositionCorrection.mXs[row]; + final float y = touchPositionCorrection.mYs[row]; + final float radius = touchPositionCorrection.mRadii[row]; + sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth; + sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight; + sweetSpotRadii[i] = radius * (float)Math.sqrt( + hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight); + } + } } mNativeProximityInfo = setProximityInfoNative(MAX_PROXIMITY_CHARS_SIZE, @@ -131,32 +150,6 @@ public class ProximityInfo { sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii); } - private void calculateSweetSpot(List<Key> keys, TouchPositionCorrection touchPositionCorrection, - float[] sweetSpotCenterXs, float[] sweetSpotCenterYs, float[] sweetSpotRadii) { - final int keyCount = keys.size(); - final float[] xs = touchPositionCorrection.mXs; - final float[] ys = touchPositionCorrection.mYs; - final float[] radii = touchPositionCorrection.mRadii; - for (int i = 0; i < keyCount; ++i) { - final Key key = keys.get(i); - final Rect hitBox = key.mHitBox; - final int row = hitBox.top / mKeyHeight; - if (row < radii.length) { - final float hitBoxCenterX = (hitBox.left + hitBox.right) * 0.5f; - final float hitBoxCenterY = (hitBox.top + hitBox.bottom) * 0.5f; - final float hitBoxWidth = hitBox.right - hitBox.left; - final float hitBoxHeight = hitBox.bottom - hitBox.top; - final float x = xs[row]; - final float y = ys[row]; - final float radius = radii[row]; - sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth; - sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight; - sweetSpotRadii[i] = radius - * (float)Math.sqrt(hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight); - } - } - } - public long getNativeProximityInfo() { return mNativeProximityInfo; } diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index cad69bb0e..7eec8e2ca 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -51,6 +51,7 @@ public class ExpandableDictionary extends Dictionary { private Object mUpdatingLock = new Object(); private static class Node { + Node() {} char mCode; int mFrequency; boolean mTerminal; @@ -547,6 +548,7 @@ public class ExpandableDictionary extends Dictionary { } private class LoadDictionaryTask extends Thread { + LoadDictionaryTask() {} @Override public void run() { loadDictionaryAsync(); diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java new file mode 100644 index 000000000..28ce916c9 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -0,0 +1,35 @@ +/* + * 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.view.inputmethod.EditorInfo; + +/** + * Class to hold attributes of the input field. + */ +public class InputAttributes { + private final String TAG = InputAttributes.class.getSimpleName(); + + // TODO: make these final + public boolean mInsertSpaceOnPickSuggestionManually; + public boolean mInputTypeNoAutoCorrect; + public boolean mIsSettingsSuggestionStripOn; + public boolean mApplicationSpecifiedCompletionOn; + + public InputAttributes(final EditorInfo editorInfo) { + } +} diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 98fea1b5b..71db4550c 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -67,6 +67,7 @@ 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.suggestions.SuggestionsView; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -195,12 +196,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private UserUnigramDictionary mUserUnigramDictionary; private boolean mIsUserDictionaryAvailable; - // TODO: Create an inner class to group options and pseudo-options to improve readability. - // These variables are initialized according to the {@link EditorInfo#inputType}. - private boolean mInsertSpaceOnPickSuggestionManually; - private boolean mInputTypeNoAutoCorrect; - private boolean mIsSettingsSuggestionStripOn; - private boolean mApplicationSpecifiedCompletionOn; + private InputAttributes mInputAttributes; private WordComposer mWordComposer = new WordComposer(); private boolean mHasUncommittedTypedChars; @@ -297,19 +293,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar || (switcher.isAlphabetMode() && switcher.isShiftedOrShiftLocked())); break; case MSG_FADEOUT_LANGUAGE_ON_SPACEBAR: - if (inputView != null) { - inputView.setSpacebarTextFadeFactor( - (1.0f + mFinalFadeoutFactorOfLanguageOnSpacebar) / 2, - (LatinKeyboard)msg.obj); - } + setSpacebarTextFadeFactor(inputView, + (1.0f + mFinalFadeoutFactorOfLanguageOnSpacebar) / 2, + (LatinKeyboard)msg.obj); sendMessageDelayed(obtainMessage(MSG_DISMISS_LANGUAGE_ON_SPACEBAR, msg.obj), mDurationOfFadeoutLanguageOnSpacebar); break; case MSG_DISMISS_LANGUAGE_ON_SPACEBAR: - if (inputView != null) { - inputView.setSpacebarTextFadeFactor(mFinalFadeoutFactorOfLanguageOnSpacebar, - (LatinKeyboard)msg.obj); - } + setSpacebarTextFadeFactor(inputView, mFinalFadeoutFactorOfLanguageOnSpacebar, + (LatinKeyboard)msg.obj); break; } } @@ -349,6 +341,19 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar sendMessage(obtainMessage(MSG_VOICE_RESULTS)); } + private static void setSpacebarTextFadeFactor(LatinKeyboardView inputView, + float fadeFactor, LatinKeyboard oldKeyboard) { + if (inputView == null) return; + final Keyboard keyboard = inputView.getKeyboard(); + if (keyboard instanceof LatinKeyboard && keyboard == oldKeyboard) { + final Key updatedKey = ((LatinKeyboard)keyboard).updateSpacebarLanguage( + fadeFactor, + Utils.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */), + SubtypeSwitcher.getInstance().needsToDisplayLanguage(keyboard.mId.mLocale)); + inputView.invalidateKey(updatedKey); + } + } + public void startDisplayLanguageOnSpacebar(boolean localeChanged) { final LatinIME latinIme = getOuterInstance(); removeMessages(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR); @@ -361,9 +366,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar || mDelayBeforeFadeoutLanguageOnSpacebar < 0; // The language is never displayed when the delay is zero. if (mDelayBeforeFadeoutLanguageOnSpacebar != 0) { - inputView.setSpacebarTextFadeFactor(needsToDisplayLanguage ? 1.0f - : mFinalFadeoutFactorOfLanguageOnSpacebar, - keyboard); + setSpacebarTextFadeFactor(inputView, + needsToDisplayLanguage ? 1.0f : mFinalFadeoutFactorOfLanguageOnSpacebar, + keyboard); } // The fadeout animation will start when the delay is positive. if (localeChanged && mDelayBeforeFadeoutLanguageOnSpacebar > 0) { @@ -489,7 +494,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar InputMethodManagerCompatWrapper.init(this); SubtypeSwitcher.init(this); KeyboardSwitcher.init(this, prefs); - AccessibilityUtils.init(this, prefs); + AccessibilityUtils.init(this); super.onCreate(); @@ -505,6 +510,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar loadSettings(); + // TODO: remove the following when it's not needed by updateCorrectionMode() any more + mInputAttributes = new InputAttributes(null); Utils.GCUtils.getInstance().reset(); boolean tryGC = true; for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { @@ -758,7 +765,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar loadSettings(); updateCorrectionMode(); - updateSuggestionVisibility(mPrefs, mResources); + updateSuggestionVisibility(mResources); if (mSuggest != null && mSettingsValues.mAutoCorrectEnabled) { mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold); @@ -788,71 +795,88 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (TRACE) Debug.startMethodTracing("/data/trace/latinime"); } - private void initializeInputAttributes(EditorInfo editorInfo) { - if (editorInfo == null) - return; - final int inputType = editorInfo.inputType; - if (inputType == InputType.TYPE_NULL) { - // TODO: We should honor TYPE_NULL specification. - Log.i(TAG, "InputType.TYPE_NULL is specified"); - } - final int inputClass = inputType & InputType.TYPE_MASK_CLASS; - final int variation = inputType & InputType.TYPE_MASK_VARIATION; - if (inputClass == 0) { - Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x imeOptions=0x%08x", - inputType, editorInfo.imeOptions)); - } - - mInsertSpaceOnPickSuggestionManually = false; - mInputTypeNoAutoCorrect = false; - mIsSettingsSuggestionStripOn = false; - mApplicationSpecifiedCompletionOn = false; - mApplicationSpecifiedCompletions = null; - - if (inputClass == InputType.TYPE_CLASS_TEXT) { - mIsSettingsSuggestionStripOn = true; + private void initializeInputAttributes(final EditorInfo editorInfo) { + mInputAttributes = new InputAttributes(editorInfo); + + final boolean insertSpaceOnPickSuggestionManually; + boolean inputTypeNoAutoCorrect = false; + boolean isSettingsSuggestionStripOn = false; + boolean applicationSpecifiedCompletionOn = false; + + if (editorInfo == null || editorInfo.inputType != InputType.TYPE_CLASS_TEXT) { + if (editorInfo.inputType == InputType.TYPE_NULL) { + // TODO: We should honor TYPE_NULL specification. + Log.i(TAG, "InputType.TYPE_NULL is specified"); + } + mApplicationSpecifiedCompletions = null; + insertSpaceOnPickSuggestionManually = false; + } else { + final int inputType = editorInfo.inputType; + final int inputClass = inputType & InputType.TYPE_MASK_CLASS; + final int variation = inputType & InputType.TYPE_MASK_VARIATION; + if (inputClass == 0) { + Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x" + + " imeOptions=0x%08x", + inputType, editorInfo.imeOptions)); + } + final boolean flagNoSuggestions = + 0 != (inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + final boolean flagMultiLine = + 0 != (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE); + final boolean flagAutoCorrect = + 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); + final boolean flagAutoComplete = + 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE); + + mApplicationSpecifiedCompletions = null; + + isSettingsSuggestionStripOn = true; // Make sure that passwords are not displayed in {@link SuggestionsView}. if (InputTypeCompatUtils.isPasswordInputType(inputType) || InputTypeCompatUtils.isVisiblePasswordInputType(inputType)) { - mIsSettingsSuggestionStripOn = false; + isSettingsSuggestionStripOn = false; } if (InputTypeCompatUtils.isEmailVariation(variation) || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) { // The point in turning this off is that we don't want to insert a space after // a name when filling a form: we can't delete trailing spaces when changing fields - mInsertSpaceOnPickSuggestionManually = false; + insertSpaceOnPickSuggestionManually = false; } else { - mInsertSpaceOnPickSuggestionManually = true; + insertSpaceOnPickSuggestionManually = true; } if (InputTypeCompatUtils.isEmailVariation(variation)) { - mIsSettingsSuggestionStripOn = false; + isSettingsSuggestionStripOn = false; } else if (variation == InputType.TYPE_TEXT_VARIATION_URI) { - mIsSettingsSuggestionStripOn = false; + isSettingsSuggestionStripOn = false; } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) { - mIsSettingsSuggestionStripOn = false; + isSettingsSuggestionStripOn = false; } else if (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) { // If it's a browser edit field and auto correct is not ON explicitly, then // disable auto correction, but keep suggestions on. - if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) { - mInputTypeNoAutoCorrect = true; + if (!flagAutoCorrect) { + inputTypeNoAutoCorrect = true; } } // If NO_SUGGESTIONS is set, don't do prediction. - if ((inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) { - mIsSettingsSuggestionStripOn = false; - mInputTypeNoAutoCorrect = true; + if (flagNoSuggestions) { + isSettingsSuggestionStripOn = false; + inputTypeNoAutoCorrect = true; } // If it's not multiline and the autoCorrect flag is not set, then don't correct - if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0 - && (inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0) { - mInputTypeNoAutoCorrect = true; + if (!flagAutoCorrect && !flagMultiLine) { + inputTypeNoAutoCorrect = true; } - if ((inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) { - mIsSettingsSuggestionStripOn = false; - mApplicationSpecifiedCompletionOn = isFullscreenMode(); + if (flagAutoComplete) { + isSettingsSuggestionStripOn = false; + applicationSpecifiedCompletionOn = isFullscreenMode(); } } + + mInputAttributes.mInsertSpaceOnPickSuggestionManually = insertSpaceOnPickSuggestionManually; + mInputAttributes.mInputTypeNoAutoCorrect = inputTypeNoAutoCorrect; + mInputAttributes.mIsSettingsSuggestionStripOn = isSettingsSuggestionStripOn; + mInputAttributes.mApplicationSpecifiedCompletionOn = applicationSpecifiedCompletionOn; } @Override @@ -1006,7 +1030,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } } - if (mApplicationSpecifiedCompletionOn) { + if (mInputAttributes.mApplicationSpecifiedCompletionOn) { mApplicationSpecifiedCompletions = applicationSpecifiedCompletions; if (applicationSpecifiedCompletions == null) { clearSuggestions(); @@ -1139,6 +1163,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (!mHasUncommittedTypedChars) return; mHasUncommittedTypedChars = false; final CharSequence typedWord = mWordComposer.getTypedWord(); + mWordComposer.onCommitWord(); if (typedWord.length() > 0) { if (ic != null) { ic.commitText(typedWord, 1); @@ -1229,7 +1254,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (isShowingOptionDialog()) return; if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) { showSubtypeSelectorAndSettings(); - } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm, false /* exclude aux subtypes */)) { + } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(false /* exclude aux subtypes */)) { showOptionsMenu(); } else { launchSettings(); @@ -1245,7 +1270,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (isShowingOptionDialog()) return false; switch (requestCode) { case CODE_SHOW_INPUT_METHOD_PICKER: - if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm, true /* include aux subtypes */)) { + if (Utils.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) { mImm.showInputMethodPicker(); return true; } @@ -1600,7 +1625,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // in Italian dov' should not be expanded to dove' because the elision // requires the last vowel to be removed. final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled - && !mInputTypeNoAutoCorrect; + && !mInputAttributes.mInputTypeNoAutoCorrect; if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) { commitCurrentAutoCorrection(primaryCode, ic); } else { @@ -1677,7 +1702,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } public boolean isSuggestionsRequested() { - return mIsSettingsSuggestionStripOn + return mInputAttributes.mIsSettingsSuggestionStripOn && (mCorrectionMode > 0 || isShowingSuggestionsStrip()); } @@ -1699,7 +1724,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return true; if (!isShowingSuggestionsStrip()) return false; - if (mApplicationSpecifiedCompletionOn) + if (mInputAttributes.mApplicationSpecifiedCompletionOn) return true; return isSuggestionsRequested(); } @@ -1786,7 +1811,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(mWordComposer, prevWord, mKeyboardSwitcher.getLatinKeyboard().getProximityInfo(), mCorrectionMode); - boolean autoCorrectionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection(); + boolean autoCorrectionAvailable = !mInputAttributes.mInputTypeNoAutoCorrect + && mSuggest.hasAutoCorrection(); final CharSequence typedWord = mWordComposer.getTypedWord(); // Here, we want to promote a whitelisted word if exists. // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid" @@ -1902,7 +1928,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (ic != null) { ic.beginBatchEdit(); } - if (mApplicationSpecifiedCompletionOn && mApplicationSpecifiedCompletions != null + if (mInputAttributes.mApplicationSpecifiedCompletionOn + && mApplicationSpecifiedCompletions != null && index >= 0 && index < mApplicationSpecifiedCompletions.length) { if (ic != null) { final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; @@ -1961,7 +1988,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar LatinImeLogger.logOnManualSuggestion(mWordComposer.getTypedWord().toString(), suggestion.toString(), index, suggestions.mWords); // Follow it with a space - if (mInsertSpaceOnPickSuggestionManually) { + if (mInputAttributes.mInsertSpaceOnPickSuggestionManually) { sendMagicSpace(); } @@ -2023,6 +2050,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } mHasUncommittedTypedChars = false; + mWordComposer.onCommitWord(); } private static final WordComposer sEmptyWordComposer = new WordComposer(); @@ -2192,10 +2220,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } ic.deleteSurroundingText(cancelLength + 1, 0); - - // Re-insert the separator + mWordComposer.resumeSuggestionOnKeptWord(); ic.commitText(mWordComposer.getTypedWord(), 1); + // Re-insert the separator ic.commitText(separator, 1); + mWordComposer.onCommitWord(); Utils.Stats.onSeparator(separator.charAt(0), WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); mHandler.cancelUpdateBigramPredictions(); @@ -2224,6 +2253,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving // the old WordComposer allows to reuse the actual typed coordinates. mHasUncommittedTypedChars = true; + mWordComposer.resumeSuggestionOnKeptWord(); ic.setComposingText(mWordComposer.getTypedWord(), 1); mHandler.cancelUpdateBigramPredictions(); mHandler.postUpdateSuggestions(); @@ -2409,13 +2439,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private void updateCorrectionMode() { // TODO: cleanup messy flags final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled - && !mInputTypeNoAutoCorrect; + && !mInputAttributes.mInputTypeNoAutoCorrect; mCorrectionMode = shouldAutoCorrect ? Suggest.CORRECTION_FULL : Suggest.CORRECTION_NONE; mCorrectionMode = (mSettingsValues.mBigramSuggestionEnabled && shouldAutoCorrect) ? Suggest.CORRECTION_FULL_BIGRAM : mCorrectionMode; } - private void updateSuggestionVisibility(final SharedPreferences prefs, final Resources res) { + private void updateSuggestionVisibility(final Resources res) { final String suggestionVisiblityStr = mSettingsValues.mShowSuggestionsSetting; for (int visibility : SUGGESTION_VISIBILITY_VALUE_ARRAY) { if (suggestionVisiblityStr.equals(res.getString(visibility))) { @@ -2508,12 +2538,14 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final Keyboard keyboard = mKeyboardSwitcher.getLatinKeyboard(); final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1; p.println(" Keyboard mode = " + keyboardMode); - p.println(" mIsSuggestionsRequested=" + mIsSettingsSuggestionStripOn); + p.println(" mIsSuggestionsRequested=" + mInputAttributes.mIsSettingsSuggestionStripOn); p.println(" mCorrectionMode=" + mCorrectionMode); p.println(" mHasUncommittedTypedChars=" + mHasUncommittedTypedChars); p.println(" mAutoCorrectEnabled=" + mSettingsValues.mAutoCorrectEnabled); - p.println(" mInsertSpaceOnPickSuggestionManually=" + mInsertSpaceOnPickSuggestionManually); - p.println(" mApplicationSpecifiedCompletionOn=" + mApplicationSpecifiedCompletionOn); + p.println(" mInsertSpaceOnPickSuggestionManually=" + + mInputAttributes.mInsertSpaceOnPickSuggestionManually); + p.println(" mApplicationSpecifiedCompletionOn=" + + mInputAttributes.mApplicationSpecifiedCompletionOn); p.println(" mSoundOn=" + mSettingsValues.mSoundOn); p.println(" mVibrateOn=" + mSettingsValues.mVibrateOn); p.println(" mKeyPreviewPopupOn=" + mSettingsValues.mKeyPreviewPopupOn); diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java index da5058dd4..6f1adfe71 100644 --- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java +++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java @@ -29,6 +29,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang public static boolean sDBG = false; public static boolean sVISUALDEBUG = false; + public static boolean sUsabilityStudy = false; @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 0ad1c1529..651d90ca4 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -52,7 +52,9 @@ public class SettingsValues { private final String mVoiceMode; private final String mAutoCorrectionThresholdRawValue; public final String mShowSuggestionsSetting; + @SuppressWarnings("unused") // TODO: Use this private final boolean mUsabilityStudyMode; + @SuppressWarnings("unused") // TODO: Use this private final String mKeyPreviewPopupDismissDelayRawValue; public final boolean mUseContactsDict; // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary @@ -60,7 +62,9 @@ public class SettingsValues { // Prediction: use bigrams to predict the next word when there is no input for it yet public final boolean mBigramPredictionEnabled; public final boolean mEnableSuggestionSpanInsertion; + @SuppressWarnings("unused") // TODO: Use this private final int mVibrationDurationSettingsRawValue; + @SuppressWarnings("unused") // TODO: Use this private final float mKeypressSoundVolumeRawValue; // Deduced settings @@ -111,12 +115,12 @@ public class SettingsValues { res.getString(R.string.auto_correction_threshold_mode_index_modest)); mShowSuggestionsSetting = prefs.getString(Settings.PREF_SHOW_SUGGESTIONS_SETTING, res.getString(R.string.prefs_suggestion_visibility_default_value)); - mUsabilityStudyMode = getUsabilityStudyMode(prefs, res); + mUsabilityStudyMode = getUsabilityStudyMode(prefs); mKeyPreviewPopupDismissDelayRawValue = prefs.getString( Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, Integer.toString(res.getInteger(R.integer.config_delay_after_preview))); mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true); - mAutoCorrectEnabled = isAutoCorrectEnabled(prefs, res, mAutoCorrectionThresholdRawValue); + mAutoCorrectEnabled = isAutoCorrectEnabled(res, mAutoCorrectionThresholdRawValue); mBigramSuggestionEnabled = mAutoCorrectEnabled && isBigramSuggestionEnabled(prefs, res, mAutoCorrectEnabled); mBigramPredictionEnabled = mBigramSuggestionEnabled @@ -131,7 +135,7 @@ public class SettingsValues { mKeypressVibrationDuration = getCurrentVibrationDuration(prefs, res); mFxVolume = getCurrentKeypressSoundVolume(prefs, res); mKeyPreviewPopupDismissDelay = getKeyPreviewPopupDismissDelay(prefs, res); - mAutoCorrectionThreshold = getAutoCorrectionThreshold(prefs, res, + mAutoCorrectionThreshold = getAutoCorrectionThreshold(res, mAutoCorrectionThresholdRawValue); mVoiceKeyEnabled = mVoiceMode != null && !mVoiceMode.equals(voiceModeOff); mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain); @@ -202,8 +206,8 @@ public class SettingsValues { return mMagicSpaceSwappers.contains(String.valueOf((char)code)); } - private static boolean isAutoCorrectEnabled(final SharedPreferences sp, - final Resources resources, final String currentAutoCorrectionSetting) { + private static boolean isAutoCorrectEnabled(final Resources resources, + final String currentAutoCorrectionSetting) { final String autoCorrectionOff = resources.getString( R.string.auto_correction_threshold_mode_index_off); return !currentAutoCorrectionSetting.equals(autoCorrectionOff); @@ -244,8 +248,8 @@ public class SettingsValues { R.bool.config_default_bigram_prediction)); } - private static double getAutoCorrectionThreshold(final SharedPreferences sp, - final Resources resources, final String currentAutoCorrectionSetting) { + private static double getAutoCorrectionThreshold(final Resources resources, + final String currentAutoCorrectionSetting) { final String[] autoCorrectionThresholdValues = resources.getStringArray( R.array.auto_correction_threshold_values); // When autoCorrectionThreshold is greater than 1.0, it's like auto correction is off. @@ -321,8 +325,7 @@ public class SettingsValues { } // Likewise - public static boolean getUsabilityStudyMode(final SharedPreferences prefs, - final Resources res) { + public static boolean getUsabilityStudyMode(final SharedPreferences prefs) { // TODO: use mUsabilityStudyMode instead of reading it again here return prefs.getBoolean(Settings.PREF_USABILITY_STUDY_MODE, true); } diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 8a4862094..42111822d 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -35,7 +35,6 @@ import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper; import com.android.inputmethod.deprecated.VoiceProxy; import com.android.inputmethod.keyboard.KeyboardSwitcher; -import com.android.inputmethod.keyboard.LatinKeyboard; import java.util.ArrayList; import java.util.Arrays; @@ -421,11 +420,7 @@ public class SubtypeSwitcher { ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); mIsNetworkConnected = !noConnection; - final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance(); - final LatinKeyboard keyboard = switcher.getLatinKeyboard(); - if (keyboard != null) { - keyboard.updateShortcutKey(isShortcutImeReady(), switcher.getKeyboardView()); - } + KeyboardSwitcher.getInstance().onNetworkStateChanged(); } ////////////////////////////////// diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java index 8c28324d8..6d6296e10 100644 --- a/java/src/com/android/inputmethod/latin/UserDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserDictionary.java @@ -18,13 +18,10 @@ package com.android.inputmethod.latin; import android.content.ContentProviderClient; import android.content.ContentResolver; -import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; import android.database.Cursor; -import android.net.Uri; -import android.os.RemoteException; import android.provider.UserDictionary.Words; import android.text.TextUtils; diff --git a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java b/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java index 6af20c754..a7f57ae46 100644 --- a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java @@ -172,7 +172,7 @@ public class UserUnigramDictionary extends ExpandableDictionary { // Nothing pending? Return if (mPendingWrites.isEmpty()) return; // Create a background thread to write the pending entries - new UpdateDbTask(getContext(), sOpenHelper, mPendingWrites, mLocale).execute(); + new UpdateDbTask(sOpenHelper, mPendingWrites, mLocale).execute(); // Create a new map for writing new entries into while the old one is written to db mPendingWrites = new HashMap<String, Integer>(); } @@ -227,8 +227,8 @@ public class UserUnigramDictionary extends ExpandableDictionary { private final DatabaseHelper mDbHelper; private final String mLocale; - public UpdateDbTask(@SuppressWarnings("unused") Context context, DatabaseHelper openHelper, - HashMap<String, Integer> pendingWrites, String locale) { + public UpdateDbTask(DatabaseHelper openHelper, HashMap<String, Integer> pendingWrites, + String locale) { mMap = pendingWrites; mLocale = locale; mDbHelper = openHelper; diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index 64f4d058b..bfa51897e 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -118,8 +118,9 @@ public class Utils { } public static boolean hasMultipleEnabledIMEsOrSubtypes( - final InputMethodManagerCompatWrapper imm, final boolean shouldIncludeAuxiliarySubtypes) { + final InputMethodManagerCompatWrapper imm = InputMethodManagerCompatWrapper.getInstance(); + if (imm == null) return false; final List<InputMethodInfoCompatWrapper> enabledImis = imm.getEnabledInputMethodList(); // Number of the filtered IMEs @@ -543,13 +544,13 @@ public class Utils { final String currentDateTimeString = new SimpleDateFormat("yyyyMMdd-HHmmssZ").format(date); if (mFile == null) { - Log.w(TAG, "No internal log file found."); + Log.w(USABILITY_TAG, "No internal log file found."); return; } if (mIms.checkCallingOrSelfPermission( android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - Log.w(TAG, "Doesn't have the permission WRITE_EXTERNAL_STORAGE"); + Log.w(USABILITY_TAG, "Doesn't have the permission WRITE_EXTERNAL_STORAGE"); return; } mWriter.flush(); @@ -563,20 +564,20 @@ public class Utils { src.close(); dest.close(); } catch (FileNotFoundException e1) { - Log.w(TAG, e1); + Log.w(USABILITY_TAG, e1); return; } catch (IOException e2) { - Log.w(TAG, e2); + Log.w(USABILITY_TAG, e2); return; } if (destFile == null || !destFile.exists()) { - Log.w(TAG, "Dest file doesn't exist."); + Log.w(USABILITY_TAG, "Dest file doesn't exist."); return; } final Intent intent = new Intent(Intent.ACTION_SEND); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (LatinImeLogger.sDBG) { - Log.d(TAG, "Destination file URI is " + destFile.toURI()); + Log.d(USABILITY_TAG, "Destination file URI is " + destFile.toURI()); } intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + destPath)); diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 8bbcff758..c0204c2a6 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -32,15 +32,39 @@ public class WordComposer { public static final int NOT_A_CODE = KeyDetector.NOT_A_CODE; public static final int NOT_A_COORDINATE = -1; - /** - * The list of unicode values for each keystroke (including surrounding keys) - */ - private ArrayList<int[]> mCodes; - - private int[] mXCoordinates; - private int[] mYCoordinates; + // Storage for all the info about the current input. + private static class CharacterStore { + /** + * The list of unicode values for each keystroke (including surrounding keys) + */ + ArrayList<int[]> mCodes; + int[] mXCoordinates; + int[] mYCoordinates; + StringBuilder mTypedWord; + CharacterStore() { + final int N = BinaryDictionary.MAX_WORD_LENGTH; + mCodes = new ArrayList<int[]>(N); + mTypedWord = new StringBuilder(N); + mXCoordinates = new int[N]; + mYCoordinates = new int[N]; + } + CharacterStore(final CharacterStore that) { + mCodes = new ArrayList<int[]>(that.mCodes); + mTypedWord = new StringBuilder(that.mTypedWord); + mXCoordinates = Arrays.copyOf(that.mXCoordinates, that.mXCoordinates.length); + mYCoordinates = Arrays.copyOf(that.mYCoordinates, that.mYCoordinates.length); + } + void reset() { + // For better performance than creating a new character store. + mCodes.clear(); + mTypedWord.setLength(0); + } + } - private StringBuilder mTypedWord; + // The currently typing word. May not be null. + private CharacterStore mCurrentWord; + // The information being kept for resuming suggestion. May be null if wiped. + private CharacterStore mWordKeptForSuggestionResuming; // An auto-correction for this word out of the dictionary. private CharSequence mAutoCorrection; @@ -56,11 +80,8 @@ public class WordComposer { private boolean mIsFirstCharCapitalized; public WordComposer() { - final int N = BinaryDictionary.MAX_WORD_LENGTH; - mCodes = new ArrayList<int[]>(N); - mTypedWord = new StringBuilder(N); - mXCoordinates = new int[N]; - mYCoordinates = new int[N]; + mCurrentWord = new CharacterStore(); + mWordKeptForSuggestionResuming = null; mTrailingSingleQuotesCount = 0; mAutoCorrection = null; } @@ -70,10 +91,8 @@ public class WordComposer { } public void init(WordComposer source) { - mCodes = new ArrayList<int[]>(source.mCodes); - mTypedWord = new StringBuilder(source.mTypedWord); - mXCoordinates = Arrays.copyOf(source.mXCoordinates, source.mXCoordinates.length); - mYCoordinates = Arrays.copyOf(source.mYCoordinates, source.mYCoordinates.length); + mCurrentWord = new CharacterStore(source.mCurrentWord); + mWordKeptForSuggestionResuming = source.mWordKeptForSuggestionResuming; mCapsCount = source.mCapsCount; mIsFirstCharCapitalized = source.mIsFirstCharCapitalized; mAutoCapitalized = source.mAutoCapitalized; @@ -85,8 +104,8 @@ public class WordComposer { * Clear out the keys registered so far. */ public void reset() { - mCodes.clear(); - mTypedWord.setLength(0); + mCurrentWord.reset(); + mWordKeptForSuggestionResuming = null; mCapsCount = 0; mIsFirstCharCapitalized = false; mTrailingSingleQuotesCount = 0; @@ -98,7 +117,7 @@ public class WordComposer { * @return the number of keystrokes */ public final int size() { - return mTypedWord.length(); + return mCurrentWord.mTypedWord.length(); } /** @@ -107,15 +126,15 @@ public class WordComposer { * @return the unicode for the pressed and surrounding keys */ public int[] getCodesAt(int index) { - return mCodes.get(index); + return mCurrentWord.mCodes.get(index); } public int[] getXCoordinates() { - return mXCoordinates; + return mCurrentWord.mXCoordinates; } public int[] getYCoordinates() { - return mYCoordinates; + return mCurrentWord.mYCoordinates; } private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) { @@ -130,12 +149,12 @@ public class WordComposer { */ public void add(int primaryCode, int[] codes, int x, int y) { final int newIndex = size(); - mTypedWord.append((char) primaryCode); + mCurrentWord.mTypedWord.append((char) primaryCode); correctPrimaryJuxtapos(primaryCode, codes); - mCodes.add(codes); + mCurrentWord.mCodes.add(codes); if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) { - mXCoordinates[newIndex] = x; - mYCoordinates[newIndex] = y; + mCurrentWord.mXCoordinates[newIndex] = x; + mCurrentWord.mYCoordinates[newIndex] = y; } mIsFirstCharCapitalized = isFirstCharCapitalized( newIndex, primaryCode, mIsFirstCharCapitalized); @@ -215,9 +234,9 @@ public class WordComposer { final int size = size(); if (size > 0) { final int lastPos = size - 1; - char lastChar = mTypedWord.charAt(lastPos); - mCodes.remove(lastPos); - mTypedWord.deleteCharAt(lastPos); + char lastChar = mCurrentWord.mTypedWord.charAt(lastPos); + mCurrentWord.mCodes.remove(lastPos); + mCurrentWord.mTypedWord.deleteCharAt(lastPos); if (Character.isUpperCase(lastChar)) mCapsCount--; } if (size() == 0) { @@ -226,8 +245,8 @@ public class WordComposer { if (mTrailingSingleQuotesCount > 0) { --mTrailingSingleQuotesCount; } else { - for (int i = mTypedWord.length() - 1; i >= 0; --i) { - if (Keyboard.CODE_SINGLE_QUOTE != mTypedWord.codePointAt(i)) break; + for (int i = mCurrentWord.mTypedWord.length() - 1; i >= 0; --i) { + if (Keyboard.CODE_SINGLE_QUOTE != mCurrentWord.mTypedWord.codePointAt(i)) break; ++mTrailingSingleQuotesCount; } } @@ -239,7 +258,7 @@ public class WordComposer { * @return the word that was typed so far. Never returns null. */ public String getTypedWord() { - return mTypedWord.toString(); + return mCurrentWord.mTypedWord.toString(); } /** @@ -306,4 +325,21 @@ public class WordComposer { public CharSequence getAutoCorrectionOrNull() { return mAutoCorrection; } + + // TODO: pass the information about what was committed and how. Was it an auto-correction? + // Was it a completion? Was is what the user typed? + public void onCommitWord() { + mWordKeptForSuggestionResuming = mCurrentWord; + // TODO: improve performance by swapping buffers instead of creating a new object. + mCurrentWord = new CharacterStore(); + } + + public boolean hasWordKeptForSuggestionResuming() { + return null != mWordKeptForSuggestionResuming; + } + + public void resumeSuggestionOnKeptWord() { + mCurrentWord = mWordKeptForSuggestionResuming; + mWordKeptForSuggestionResuming = null; + } } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java index 9a2bebfdf..2bc2cfdf6 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java @@ -43,7 +43,7 @@ public class SpellCheckerProximityInfo { return result; } - static class Latin { + private static class Latin { // This is a map from the code point to the index in the PROXIMITY array. // At the time the native code to read the binary dictionary needs the proximity info be // passed as a flat array spaced by MAX_PROXIMITY_CHARS_SIZE columns, one for each input @@ -62,7 +62,7 @@ public class SpellCheckerProximityInfo { // to spell check has been entered with one of the keyboards above. Also, specifically // to English, many spelling errors consist of the last vowel of the word being wrong // because in English vowels tend to merge with each other in pronunciation. - final private static int[] PROXIMITY = { + final static int[] PROXIMITY = { 'q', 'w', 's', 'a', 'z', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'w', 'q', 'a', 's', 'd', 'e', 'x', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, 'e', 'w', 's', 'd', 'f', 'r', 'a', 'i', 'o', 'u', NUL, NUL, NUL, NUL, NUL, NUL, @@ -101,14 +101,14 @@ public class SpellCheckerProximityInfo { static { buildProximityIndices(PROXIMITY, INDICES); } - private static int getIndexOf(int characterCode) { + static int getIndexOf(int characterCode) { return computeIndex(characterCode, INDICES); } } - static class Cyrillic { + private static class Cyrillic { final private static TreeMap<Integer, Integer> INDICES = new TreeMap<Integer, Integer>(); - final private static int[] PROXIMITY = { + final static int[] PROXIMITY = { // TODO: This table is solely based on the keyboard layout. Consult with Russian // speakers on commonly misspelled words/letters. 'ะน', 'ั', 'ั', 'ั', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, @@ -150,7 +150,7 @@ public class SpellCheckerProximityInfo { static { buildProximityIndices(PROXIMITY, INDICES); } - private static int getIndexOf(int characterCode) { + static int getIndexOf(int characterCode) { return computeIndex(characterCode, INDICES); } } diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java index 86072b64b..7f5918991 100644 --- a/java/src/com/android/inputmethod/latin/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.suggestions; import android.content.res.Resources; import android.graphics.Paint; @@ -27,11 +27,12 @@ import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.keyboard.internal.KeyboardParams; +import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; public class MoreSuggestions extends Keyboard { - private static final boolean DBG = LatinImeLogger.sDBG; - public static final int SUGGESTION_CODE_BASE = 1024; private MoreSuggestions(Builder.MoreSuggestionsParam params) { @@ -39,6 +40,8 @@ public class MoreSuggestions extends Keyboard { } public static class Builder extends KeyboardBuilder<Builder.MoreSuggestionsParam> { + private static final boolean DBG = LatinImeLogger.sDBG; + private final MoreSuggestionsView mPaneView; private SuggestedWords mSuggestions; private int mFromPos; diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java index c61dd6313..b5f67ace0 100644 --- a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.suggestions; import android.content.Context; import android.content.res.Resources; @@ -34,6 +34,7 @@ import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.PointerTracker.KeyEventHandler; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; +import com.android.inputmethod.latin.R; /** * A view that renders a virtual {@link MoreSuggestions}. It handles rendering of keys and detecting diff --git a/java/src/com/android/inputmethod/latin/SuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java index 10f5ec9db..40d782640 100644 --- a/java/src/com/android/inputmethod/latin/SuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.suggestions; import android.content.Context; import android.content.res.Resources; @@ -57,7 +57,12 @@ import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.MoreKeysPanel; import com.android.inputmethod.keyboard.PointerTracker; +import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.StaticInnerHandlerWrapper; +import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.Utils; import java.util.ArrayList; import java.util.List; @@ -72,7 +77,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}. public static final int MAX_SUGGESTIONS = 18; - private static final boolean DBG = LatinImeLogger.sDBG; + static final boolean DBG = LatinImeLogger.sDBG; private final ViewGroup mSuggestionsStrip; private KeyboardView mKeyboardView; @@ -100,8 +105,6 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, private static class UiHandler extends StaticInnerHandlerWrapper<SuggestionsView> { private static final int MSG_HIDE_PREVIEW = 0; - private static final long DELAY_HIDE_PREVIEW = 1300; - public UiHandler(SuggestionsView outerInstance) { super(outerInstance); } @@ -116,11 +119,6 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, } } - public void postHidePreview() { - cancelHidePreview(); - sendMessageDelayed(obtainMessage(MSG_HIDE_PREVIEW), DELAY_HIDE_PREVIEW); - } - public void cancelHidePreview() { removeMessages(MSG_HIDE_PREVIEW); } @@ -148,6 +146,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, private final List<View> mDividers; private final List<TextView> mInfos; + private final int mColorValidTypedWord; private final int mColorTypedWord; private final int mColorAutoCorrect; private final int mColorSuggested; @@ -191,6 +190,8 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.SuggestionsView, defStyle, R.style.SuggestionsViewStyle); mSuggestionStripOption = a.getInt(R.styleable.SuggestionsView_suggestionStripOption, 0); + final float alphaValidTypedWord = getPercent(a, + R.styleable.SuggestionsView_alphaValidTypedWord, 100); final float alphaTypedWord = getPercent(a, R.styleable.SuggestionsView_alphaTypedWord, 100); final float alphaAutoCorrect = getPercent(a, @@ -198,6 +199,9 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, final float alphaSuggested = getPercent(a, R.styleable.SuggestionsView_alphaSuggested, 100); mAlphaObsoleted = getPercent(a, R.styleable.SuggestionsView_alphaSuggested, 100); + mColorValidTypedWord = applyAlpha( + a.getColor(R.styleable.SuggestionsView_colorValidTypedWord, 0), + alphaValidTypedWord); mColorTypedWord = applyAlpha( a.getColor(R.styleable.SuggestionsView_colorTypedWord, 0), alphaTypedWord); mColorAutoCorrect = applyAlpha( @@ -295,6 +299,8 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, final int color; if (index == mCenterSuggestionIndex && Utils.willAutoCorrect(suggestions)) { color = mColorAutoCorrect; + } else if (index == mCenterSuggestionIndex && suggestions.mTypedWordValid) { + color = mColorValidTypedWord; } else if (isSuggested) { color = mColorSuggested; } else { @@ -430,7 +436,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, final TextView word = mWords.get(index); word.setEnabled(true); - word.setTextColor(mColorTypedWord); + word.setTextColor(mColorAutoCorrect); final CharSequence text = suggestions.getWord(index); word.setText(text); word.setTextScaleX(1.0f); @@ -468,6 +474,91 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, setLayoutWeight( hintView, 1.0f - mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); } + + private static CharSequence getDebugInfo(SuggestedWords suggestions, int pos) { + if (DBG && pos < suggestions.size()) { + final SuggestedWordInfo wordInfo = suggestions.getInfo(pos); + if (wordInfo != null) { + final CharSequence debugInfo = wordInfo.getDebugString(); + if (!TextUtils.isEmpty(debugInfo)) { + return debugInfo; + } + } + } + return null; + } + + private static void setLayoutWeight(View v, float weight, int height) { + final ViewGroup.LayoutParams lp = v.getLayoutParams(); + if (lp instanceof LinearLayout.LayoutParams) { + final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp; + llp.weight = weight; + llp.width = 0; + llp.height = height; + } + } + + private static float getTextScaleX(CharSequence text, int maxWidth, TextPaint paint) { + paint.setTextScaleX(1.0f); + final int width = getTextWidth(text, paint); + if (width <= maxWidth) { + return 1.0f; + } + return maxWidth / (float)width; + } + + private static CharSequence getEllipsizedText(CharSequence text, int maxWidth, + TextPaint paint) { + if (text == null) return null; + paint.setTextScaleX(1.0f); + final int width = getTextWidth(text, paint); + if (width <= maxWidth) { + return text; + } + final float scaleX = maxWidth / (float)width; + if (scaleX >= MIN_TEXT_XSCALE) { + paint.setTextScaleX(scaleX); + return text; + } + + // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To + // get squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE). + final CharSequence ellipsized = TextUtils.ellipsize( + text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE); + paint.setTextScaleX(MIN_TEXT_XSCALE); + return ellipsized; + } + + private static int getTextWidth(CharSequence text, TextPaint paint) { + if (TextUtils.isEmpty(text)) return 0; + final Typeface savedTypeface = paint.getTypeface(); + paint.setTypeface(getTextTypeface(text)); + final int len = text.length(); + final float[] widths = new float[len]; + final int count = paint.getTextWidths(text, 0, len, widths); + int width = 0; + for (int i = 0; i < count; i++) { + width += Math.round(widths[i] + 0.5f); + } + paint.setTypeface(savedTypeface); + return width; + } + + private static Typeface getTextTypeface(CharSequence text) { + if (!(text instanceof SpannableString)) + return Typeface.DEFAULT; + + final SpannableString ss = (SpannableString)text; + final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class); + if (styles.length == 0) + return Typeface.DEFAULT; + + switch (styles[0].getStyle()) { + case Typeface.BOLD: return Typeface.DEFAULT_BOLD; + // TODO: BOLD_ITALIC, ITALIC case? + default: return Typeface.DEFAULT; + } + } } /** @@ -554,90 +645,6 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, mParams.layout(mSuggestions, mSuggestionsStrip, this, getWidth()); } - private static CharSequence getDebugInfo(SuggestedWords suggestions, int pos) { - if (DBG && pos < suggestions.size()) { - final SuggestedWordInfo wordInfo = suggestions.getInfo(pos); - if (wordInfo != null) { - final CharSequence debugInfo = wordInfo.getDebugString(); - if (!TextUtils.isEmpty(debugInfo)) { - return debugInfo; - } - } - } - return null; - } - - private static void setLayoutWeight(View v, float weight, int height) { - final ViewGroup.LayoutParams lp = v.getLayoutParams(); - if (lp instanceof LinearLayout.LayoutParams) { - final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp; - llp.weight = weight; - llp.width = 0; - llp.height = height; - } - } - - private static float getTextScaleX(CharSequence text, int maxWidth, TextPaint paint) { - paint.setTextScaleX(1.0f); - final int width = getTextWidth(text, paint); - if (width <= maxWidth) { - return 1.0f; - } - return maxWidth / (float)width; - } - - private static CharSequence getEllipsizedText(CharSequence text, int maxWidth, - TextPaint paint) { - if (text == null) return null; - paint.setTextScaleX(1.0f); - final int width = getTextWidth(text, paint); - if (width <= maxWidth) { - return text; - } - final float scaleX = maxWidth / (float)width; - if (scaleX >= MIN_TEXT_XSCALE) { - paint.setTextScaleX(scaleX); - return text; - } - - // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To get - // squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE). - final CharSequence ellipsized = TextUtils.ellipsize( - text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE); - paint.setTextScaleX(MIN_TEXT_XSCALE); - return ellipsized; - } - - private static int getTextWidth(CharSequence text, TextPaint paint) { - if (TextUtils.isEmpty(text)) return 0; - final Typeface savedTypeface = paint.getTypeface(); - paint.setTypeface(getTextTypeface(text)); - final int len = text.length(); - final float[] widths = new float[len]; - final int count = paint.getTextWidths(text, 0, len, widths); - int width = 0; - for (int i = 0; i < count; i++) { - width += Math.round(widths[i] + 0.5f); - } - paint.setTypeface(savedTypeface); - return width; - } - - private static Typeface getTextTypeface(CharSequence text) { - if (!(text instanceof SpannableString)) - return Typeface.DEFAULT; - - final SpannableString ss = (SpannableString)text; - final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class); - if (styles.length == 0) - return Typeface.DEFAULT; - - switch (styles[0].getStyle()) { - case Typeface.BOLD: return Typeface.DEFAULT_BOLD; - // TODO: BOLD_ITALIC, ITALIC case? - default: return Typeface.DEFAULT; - } - } public boolean isShowingAddToDictionaryHint() { return mSuggestionsStrip.getChildCount() > 0 |