diff options
Diffstat (limited to 'java/src')
45 files changed, 752 insertions, 462 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java index 70e38fdb0..039c77b9c 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java @@ -35,6 +35,7 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.latin.CollectionUtils; /** * Exposes a virtual view sub-tree for {@link KeyboardView} and generates @@ -55,7 +56,7 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat private final AccessibilityUtils mAccessibilityUtils; /** A map of integer IDs to {@link Key}s. */ - private final SparseArray<Key> mVirtualViewIdToKey = new SparseArray<Key>(); + private final SparseArray<Key> mVirtualViewIdToKey = CollectionUtils.newSparseArray(); /** Temporary rect used to calculate in-screen bounds. */ private final Rect mTempBoundsInScreen = new Rect(); diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 9b74070af..5c45448a5 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -25,6 +25,7 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.R; import java.util.HashMap; @@ -38,7 +39,7 @@ public class KeyCodeDescriptionMapper { private static KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper(); // Map of key labels to spoken description resource IDs - private final HashMap<CharSequence, Integer> mKeyLabelMap; + private final HashMap<CharSequence, Integer> mKeyLabelMap = CollectionUtils.newHashMap(); // Sparse array of spoken description resource IDs indexed by key codes private final SparseIntArray mKeyCodeMap; @@ -52,7 +53,6 @@ public class KeyCodeDescriptionMapper { } private KeyCodeDescriptionMapper() { - mKeyLabelMap = new HashMap<CharSequence, Integer>(); mKeyCodeMap = new SparseIntArray(); } diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java index 1183b5fb9..6ba309fcb 100644 --- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java +++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java @@ -16,10 +16,6 @@ package com.android.inputmethod.compat; -import com.android.inputmethod.latin.LatinImeLogger; -import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver; - import android.content.Context; import android.text.Spannable; import android.text.SpannableString; @@ -27,6 +23,11 @@ import android.text.Spanned; import android.text.TextUtils; import android.util.Log; +import com.android.inputmethod.latin.CollectionUtils; +import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver; + import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.ArrayList; @@ -119,7 +120,7 @@ public class SuggestionSpanUtils { } else { spannable = new SpannableString(pickedWord); } - final ArrayList<String> suggestionsList = new ArrayList<String>(); + final ArrayList<String> suggestionsList = CollectionUtils.newArrayList(); for (int i = 0; i < suggestedWords.size(); ++i) { if (suggestionsList.size() >= OBJ_SUGGESTIONS_MAX_SIZE) { break; diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 489cc37ea..e37868b3f 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -33,6 +33,7 @@ import com.android.inputmethod.keyboard.internal.KeyStyles; import com.android.inputmethod.keyboard.internal.KeyboardCodesSet; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardTextsSet; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.LocaleUtils.RunInLocale; import com.android.inputmethod.latin.R; @@ -134,7 +135,7 @@ public class Keyboard { public final Key[] mAltCodeKeysWhileTyping; public final KeyboardIconsSet mIconsSet; - private final SparseArray<Key> mKeyCache = new SparseArray<Key>(); + private final SparseArray<Key> mKeyCache = CollectionUtils.newSparseArray(); private final ProximityInfo mProximityInfo; private final boolean mProximityCharsCorrectionEnabled; @@ -254,9 +255,9 @@ public class Keyboard { public int GRID_WIDTH; public int GRID_HEIGHT; - public final HashSet<Key> mKeys = new HashSet<Key>(); - public final ArrayList<Key> mShiftKeys = new ArrayList<Key>(); - public final ArrayList<Key> mAltCodeKeysWhileTyping = new ArrayList<Key>(); + public final HashSet<Key> mKeys = CollectionUtils.newHashSet(); + public final ArrayList<Key> mShiftKeys = CollectionUtils.newArrayList(); + public final ArrayList<Key> mAltCodeKeysWhileTyping = CollectionUtils.newArrayList(); public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet(); public final KeyboardCodesSet mCodesSet = new KeyboardCodesSet(); public final KeyboardTextsSet mTextsSet = new KeyboardTextsSet(); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 64b3f0952..76ac3de22 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -36,6 +36,7 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.EditorInfoCompatUtils; import com.android.inputmethod.keyboard.KeyboardLayoutSet.Params.ElementParams; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.InputTypeUtils; import com.android.inputmethod.latin.LatinImeLogger; @@ -71,7 +72,7 @@ public class KeyboardLayoutSet { private final Params mParams; private static final HashMap<KeyboardId, SoftReference<Keyboard>> sKeyboardCache = - new HashMap<KeyboardId, SoftReference<Keyboard>>(); + CollectionUtils.newHashMap(); private static final KeysCache sKeysCache = new KeysCache(); public static class KeyboardLayoutSetException extends RuntimeException { @@ -84,11 +85,7 @@ public class KeyboardLayoutSet { } public static class KeysCache { - private final HashMap<Key, Key> mMap; - - public KeysCache() { - mMap = new HashMap<Key, Key>(); - } + private final HashMap<Key, Key> mMap = CollectionUtils.newHashMap(); public void clear() { mMap.clear(); @@ -120,7 +117,7 @@ public class KeyboardLayoutSet { int mWidth; // Sparse array of KeyboardLayoutSet element parameters indexed by element's id. final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap = - new SparseArray<ElementParams>(); + CollectionUtils.newSparseArray(); static class ElementParams { int mKeyboardXmlId; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 0232fae4f..fd789f029 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -21,7 +21,6 @@ import android.content.SharedPreferences; import android.content.res.Resources; import android.util.Log; import android.view.ContextThemeWrapper; -import android.view.InflateException; import android.view.LayoutInflater; import android.view.View; import android.view.inputmethod.EditorInfo; @@ -38,7 +37,6 @@ import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SettingsValues; import com.android.inputmethod.latin.SubtypeSwitcher; -import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.WordComposer; public class KeyboardSwitcher implements KeyboardState.SwitchActions { @@ -47,24 +45,24 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916"; static class KeyboardTheme { - public final String mName; public final int mThemeId; public final int mStyleId; - public KeyboardTheme(String name, int themeId, int styleId) { - mName = name; + // Note: The themeId should be aligned with "themeId" attribute of Keyboard style + // in values/style.xml. + public KeyboardTheme(int themeId, int styleId) { mThemeId = themeId; mStyleId = styleId; } } private static final KeyboardTheme[] KEYBOARD_THEMES = { - new KeyboardTheme("Basic", 0, R.style.KeyboardTheme), - new KeyboardTheme("HighContrast", 1, R.style.KeyboardTheme_HighContrast), - new KeyboardTheme("Stone", 6, R.style.KeyboardTheme_Stone), - new KeyboardTheme("Stone.Bold", 7, R.style.KeyboardTheme_Stone_Bold), - new KeyboardTheme("GingerBread", 8, R.style.KeyboardTheme_Gingerbread), - new KeyboardTheme("IceCreamSandwich", 5, R.style.KeyboardTheme_IceCreamSandwich), + new KeyboardTheme(0, R.style.KeyboardTheme), + new KeyboardTheme(1, R.style.KeyboardTheme_HighContrast), + new KeyboardTheme(6, R.style.KeyboardTheme_Stone), + new KeyboardTheme(7, R.style.KeyboardTheme_Stone_Bold), + new KeyboardTheme(8, R.style.KeyboardTheme_Gingerbread), + new KeyboardTheme(5, R.style.KeyboardTheme_IceCreamSandwich), }; private SubtypeSwitcher mSubtypeSwitcher; @@ -355,22 +353,9 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { mKeyboardView.closing(); } - Utils.GCUtils.getInstance().reset(); - boolean tryGC = true; - for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { - try { - setContextThemeWrapper(mLatinIME, mKeyboardTheme); - mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate( - R.layout.input_view, null); - tryGC = false; - } catch (OutOfMemoryError e) { - Log.w(TAG, "load keyboard failed: " + e); - tryGC = Utils.GCUtils.getInstance().tryGCOrWait(mKeyboardTheme.mName, e); - } catch (InflateException e) { - Log.w(TAG, "load keyboard failed: " + e); - tryGC = Utils.GCUtils.getInstance().tryGCOrWait(mKeyboardTheme.mName, e); - } - } + setContextThemeWrapper(mLatinIME, mKeyboardTheme); + mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate( + R.layout.input_view, null); mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view); if (isHardwareAcceleratedDrawingEnabled) { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index fe14c3ffc..0a70605d7 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -39,6 +39,7 @@ import android.view.ViewGroup; import android.widget.TextView; import com.android.inputmethod.keyboard.internal.PreviewPlacerView; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; @@ -106,6 +107,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { // Key preview private final int mKeyPreviewLayoutId; + private final SparseArray<TextView> mKeyPreviewTexts = CollectionUtils.newSparseArray(); protected final KeyPreviewDrawParams mKeyPreviewDrawParams; private boolean mShowKeyPreviewPopup = true; private int mDelayAfterPreview; @@ -117,7 +119,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { /** True if all keys should be drawn */ private boolean mInvalidateAllKeys; /** The keys that should be drawn */ - private final HashSet<Key> mInvalidatedKeys = new HashSet<Key>(); + private final HashSet<Key> mInvalidatedKeys = CollectionUtils.newHashSet(); /** The working rectangle variable */ private final Rect mWorkingRect = new Rect(); /** The keyboard bitmap buffer for faster updates */ @@ -129,9 +131,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { private final Paint mPaint = new Paint(); private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); // This sparse array caches key label text height in pixel indexed by key label text size. - private static final SparseArray<Float> sTextHeightCache = new SparseArray<Float>(); + private static final SparseArray<Float> sTextHeightCache = CollectionUtils.newSparseArray(); // This sparse array caches key label text width in pixel indexed by key label text size. - private static final SparseArray<Float> sTextWidthCache = new SparseArray<Float>(); + private static final SparseArray<Float> sTextWidthCache = CollectionUtils.newSparseArray(); private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' }; private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' }; @@ -151,7 +153,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { final PointerTracker tracker = (PointerTracker) msg.obj; switch (msg.what) { case MSG_DISMISS_KEY_PREVIEW: - tracker.getKeyPreviewText().setVisibility(View.INVISIBLE); + final TextView previewText = keyboardView.mKeyPreviewTexts.get(tracker.mPointerId); + if (previewText != null) { + previewText.setVisibility(INVISIBLE); + } break; } } @@ -164,7 +169,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker); } - public void cancelAllDismissKeyPreviews() { + private void cancelAllDismissKeyPreviews() { removeMessages(MSG_DISMISS_KEY_PREVIEW); } @@ -906,15 +911,30 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } } - // Called by {@link PointerTracker} constructor to create a TextView. - @Override - public TextView inflateKeyPreviewText() { + private TextView getKeyPreviewText(final int pointerId) { + TextView previewText = mKeyPreviewTexts.get(pointerId); + if (previewText != null) { + return previewText; + } final Context context = getContext(); if (mKeyPreviewLayoutId != 0) { - return (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null); + previewText = (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null); } else { - return new TextView(context); + previewText = new TextView(context); + } + mKeyPreviewTexts.put(pointerId, previewText); + return previewText; + } + + private void dismissAllKeyPreviews() { + final int pointerCount = mKeyPreviewTexts.size(); + for (int id = 0; id < pointerCount; id++) { + final TextView previewText = mKeyPreviewTexts.get(id); + if (previewText != null) { + previewText.setVisibility(INVISIBLE); + } } + PointerTracker.setReleasedKeyGraphicsToAllKeys(); } @Override @@ -970,7 +990,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { public void showKeyPreview(PointerTracker tracker) { if (!mShowKeyPreviewPopup) return; - final TextView previewText = tracker.getKeyPreviewText(); + final TextView previewText = getKeyPreviewText(tracker.mPointerId); // If the key preview has no parent view yet, add it to the ViewGroup which can place // key preview absolutely in SoftInputWindow. if (previewText.getParent() == null) { @@ -1081,7 +1101,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } public void closing() { - PointerTracker.dismissAllKeyPreviews(); + dismissAllKeyPreviews(); cancelAllMessages(); mInvalidateAllKeys = true; diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index f572cb80d..1b309a439 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -528,7 +528,17 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key // to properly show the splash screen, which requires that the window token of the // KeyboardView be non-null. if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(); + ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(this); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + // Notify the research logger that the keyboard view has been detached. This is needed + // to invalidate the reference of {@link MainKeyboardView} to null. + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.getInstance().mainKeyboardView_onDetachedFromWindow(); } } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 7d565a64f..d9a7cb418 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -21,12 +21,11 @@ import android.graphics.Paint; import android.os.SystemClock; import android.util.Log; import android.view.MotionEvent; -import android.view.View; -import android.widget.TextView; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.keyboard.internal.GestureStroke; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.define.ProductionFlag; @@ -78,7 +77,6 @@ public class PointerTracker implements PointerTrackerQueue.Element { public interface DrawingProxy extends MoreKeysPanel.Controller { public void invalidateKey(Key key); - public TextView inflateKeyPreviewText(); public void showKeyPreview(PointerTracker tracker); public void dismissKeyPreview(PointerTracker tracker); public void showGestureTrail(PointerTracker tracker); @@ -125,7 +123,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { private static int sTouchNoiseThresholdDistanceSquared; private static boolean sNeedsPhantomSuddenMoveEventHack; - private static final ArrayList<PointerTracker> sTrackers = new ArrayList<PointerTracker>(); + private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList(); private static final InputPointers sAggregratedPointers = new InputPointers( GestureStroke.DEFAULT_CAPACITY); private static PointerTrackerQueue sPointerTrackerQueue; @@ -139,7 +137,6 @@ public class PointerTracker implements PointerTrackerQueue.Element { private Keyboard mKeyboard; private int mKeyQuarterWidthSquared; - private final TextView mKeyPreviewText; private boolean mIsAlphabetKeyboard; private boolean mIsPossibleGesture = false; @@ -260,11 +257,10 @@ public class PointerTracker implements PointerTrackerQueue.Element { updateGestureHandlingMode(); } - public static void dismissAllKeyPreviews() { + public static void setReleasedKeyGraphicsToAllKeys() { final int trackersSize = sTrackers.size(); for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); - tracker.getKeyPreviewText().setVisibility(View.INVISIBLE); tracker.setReleasedKeyGraphics(tracker.mCurrentKey); } } @@ -311,11 +307,6 @@ public class PointerTracker implements PointerTrackerQueue.Element { mListener = handler.getKeyboardActionListener(); mDrawingProxy = handler.getDrawingProxy(); mTimerProxy = handler.getTimerProxy(); - mKeyPreviewText = mDrawingProxy.inflateKeyPreviewText(); - } - - public TextView getKeyPreviewText() { - return mKeyPreviewText; } // Returns true if keyboard has been changed by this callback. diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index 94a7b826f..13214bb9f 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -21,6 +21,7 @@ import static com.android.inputmethod.keyboard.Keyboard.CODE_UNSPECIFIED; import android.text.TextUtils; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.StringUtils; @@ -258,7 +259,7 @@ public class KeySpecParser { throw new IllegalArgumentException(); } - final ArrayList<T> list = new ArrayList<T>(end - start); + final ArrayList<T> list = CollectionUtils.newArrayList(end - start); for (int i = start; i < end; i++) { list.add(array[i]); } @@ -438,7 +439,7 @@ public class KeySpecParser { // Skip empty entry. if (pos - start > 0) { if (list == null) { - list = new ArrayList<String>(); + list = CollectionUtils.newArrayList(); } list.add(text.substring(start, pos)); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index 291b3b943..e40cf45cc 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java @@ -21,6 +21,7 @@ import android.util.Log; import android.util.SparseArray; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.XmlParseUtils; @@ -33,7 +34,7 @@ public class KeyStyles { private static final String TAG = KeyStyles.class.getSimpleName(); private static final boolean DEBUG = false; - final HashMap<String, KeyStyle> mStyles = new HashMap<String, KeyStyle>(); + final HashMap<String, KeyStyle> mStyles = CollectionUtils.newHashMap(); final KeyboardTextsSet mTextsSet; private final KeyStyle mEmptyKeyStyle; @@ -90,7 +91,7 @@ public class KeyStyles { private class DeclaredKeyStyle extends KeyStyle { private final String mParentStyleName; - private final SparseArray<Object> mStyleAttributes = new SparseArray<Object>(); + private final SparseArray<Object> mStyleAttributes = CollectionUtils.newSparseArray(); public DeclaredKeyStyle(String parentStyleName) { mParentStyleName = parentStyleName; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java index f7981a320..f7923d0b9 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java @@ -17,13 +17,13 @@ package com.android.inputmethod.keyboard.internal; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.CollectionUtils; import java.util.HashMap; public class KeyboardCodesSet { - private static final HashMap<String, int[]> sLanguageToCodesMap = - new HashMap<String, int[]>(); - private static final HashMap<String, Integer> sNameToIdMap = new HashMap<String, Integer>(); + private static final HashMap<String, int[]> sLanguageToCodesMap = CollectionUtils.newHashMap(); + private static final HashMap<String, Integer> sNameToIdMap = CollectionUtils.newHashMap(); private int[] mCodes = DEFAULT; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index 5155851fe..4a98a3698 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -22,6 +22,7 @@ import android.graphics.drawable.Drawable; import android.util.Log; import android.util.SparseIntArray; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.R; import java.util.HashMap; @@ -35,7 +36,7 @@ public class KeyboardIconsSet { private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray(); // Icon name to icon id map. - private static final HashMap<String, Integer> sNameToIdsMap = new HashMap<String, Integer>(); + private static final HashMap<String, Integer> sNameToIdsMap = CollectionUtils.newHashMap(); private static final Object[] NAMES_AND_ATTR_IDS = { "undefined", ATTR_UNDEFINED, diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index bec0f1fef..a608cdef0 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal; import android.content.Context; import android.content.res.Resources; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.R; import java.util.HashMap; @@ -45,14 +46,12 @@ import java.util.HashMap; */ public final class KeyboardTextsSet { // Language to texts map. - private static final HashMap<String, String[]> sLocaleToTextsMap = - new HashMap<String, String[]>(); - private static final HashMap<String, Integer> sNameToIdsMap = - new HashMap<String, Integer>(); + private static final HashMap<String, String[]> sLocaleToTextsMap = CollectionUtils.newHashMap(); + private static final HashMap<String, Integer> sNameToIdsMap = CollectionUtils.newHashMap(); private String[] mTexts; // Resource name to text map. - private HashMap<String, String> mResourceNameToTextsMap = new HashMap<String, String>(); + private HashMap<String, String> mResourceNameToTextsMap = CollectionUtils.newHashMap(); public void setLanguage(final String language) { mTexts = sLocaleToTextsMap.get(language); diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java index 1c7ceaf92..e0858c019 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java @@ -18,6 +18,8 @@ package com.android.inputmethod.keyboard.internal; import android.util.Log; +import com.android.inputmethod.latin.CollectionUtils; + import java.util.ArrayList; public class PointerTrackerQueue { @@ -32,7 +34,7 @@ public class PointerTrackerQueue { private static final int INITIAL_CAPACITY = 10; private final ArrayList<Element> mExpandableArrayOfActivePointers = - new ArrayList<Element>(INITIAL_CAPACITY); + CollectionUtils.newArrayList(INITIAL_CAPACITY); private int mArraySize = 0; public synchronized int size() { diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java index 59a92d625..3f33aee5a 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java @@ -28,6 +28,7 @@ import android.util.SparseArray; import android.widget.RelativeLayout; import com.android.inputmethod.keyboard.PointerTracker; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; @@ -47,7 +48,7 @@ public class PreviewPlacerView extends RelativeLayout { private int mXOrigin; private int mYOrigin; - private final SparseArray<PointerTracker> mPointers = new SparseArray<PointerTracker>(); + private final SparseArray<PointerTracker> mPointers = CollectionUtils.newSparseArray(); private String mGestureFloatingPreviewText; private int mLastPointerX; diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java index f8f1395b3..4b47a261f 100644 --- a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java +++ b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java @@ -91,7 +91,7 @@ public class AdditionalSubtype { } final String[] prefSubtypeArray = prefSubtypes.split(PREF_SUBTYPE_SEPARATOR); final ArrayList<InputMethodSubtype> subtypesList = - new ArrayList<InputMethodSubtype>(prefSubtypeArray.length); + CollectionUtils.newArrayList(prefSubtypeArray.length); for (final String prefSubtype : prefSubtypeArray) { final InputMethodSubtype subtype = createAdditionalSubtype(prefSubtype); if (subtype.getNameResId() == SubtypeLocale.UNKNOWN_KEYBOARD_LAYOUT) { diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java index 779a38823..d01592a4d 100644 --- a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java +++ b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java @@ -89,7 +89,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment { super(context, android.R.layout.simple_spinner_item); setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - final TreeSet<SubtypeLocaleItem> items = new TreeSet<SubtypeLocaleItem>(); + final TreeSet<SubtypeLocaleItem> items = CollectionUtils.newTreeSet(); final InputMethodInfo imi = ImfUtils.getInputMethodInfoOfThisIme(context); final int count = imi.getSubtypeCount(); for (int i = 0; i < count; i++) { @@ -533,7 +533,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment { private InputMethodSubtype[] getSubtypes() { final PreferenceGroup group = getPreferenceScreen(); - final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); + final ArrayList<InputMethodSubtype> subtypes = CollectionUtils.newArrayList(); final int count = group.getPreferenceCount(); for (int i = 0; i < count; i++) { final Preference pref = group.getPreference(i); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index f6635847b..8909526d8 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -63,7 +63,7 @@ public class BinaryDictionary extends Dictionary { private final boolean mUseFullEditDistance; private final SparseArray<DicTraverseSession> mDicTraverseSessions = - new SparseArray<DicTraverseSession>(); + CollectionUtils.newSparseArray(); // TODO: There should be a way to remove used DicTraverseSession objects from // {@code mDicTraverseSessions}. @@ -160,7 +160,7 @@ public class BinaryDictionary extends Dictionary { mUseFullEditDistance, mOutputChars, mOutputScores, mSpaceIndices, mOutputTypes); final int count = Math.min(tmpCount, MAX_PREDICTIONS); - final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>(); + final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); for (int j = 0; j < count; ++j) { if (composerSize > 0 && mOutputScores[j] < 1) break; final int start = j * MAX_WORD_LENGTH; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 236c198ad..799aea8ef 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -99,7 +99,7 @@ public class BinaryDictionaryFileDumper { } try { - final List<WordListInfo> list = new ArrayList<WordListInfo>(); + final List<WordListInfo> list = CollectionUtils.newArrayList(); do { final String wordListId = c.getString(0); final String wordListLocale = c.getString(1); @@ -267,7 +267,7 @@ public class BinaryDictionaryFileDumper { final ContentResolver resolver = context.getContentResolver(); final List<WordListInfo> idList = getWordListWordListInfos(locale, context, hasDefaultWordList); - final List<AssetFileAddress> fileAddressList = new ArrayList<AssetFileAddress>(); + final List<AssetFileAddress> fileAddressList = CollectionUtils.newArrayList(); for (WordListInfo id : idList) { final AssetFileAddress afd = cacheWordList(id.mId, id.mLocale, resolver, context); if (null != afd) { diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index dd11aaa37..4ada909de 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -260,8 +260,7 @@ class BinaryDictionaryGetter { final Context context) { final File[] directoryList = getCachedDirectoryList(context); if (null == directoryList) return EMPTY_FILE_ARRAY; - final HashMap<String, FileAndMatchLevel> cacheFiles = - new HashMap<String, FileAndMatchLevel>(); + final HashMap<String, FileAndMatchLevel> cacheFiles = CollectionUtils.newHashMap(); for (File directory : directoryList) { if (!directory.isDirectory()) continue; final String dirLocale = getWordListIdFromFileName(directory.getName()); @@ -359,7 +358,7 @@ class BinaryDictionaryGetter { } final int formatVersion = raf.readInt(); final int headerSize = raf.readInt(); - final HashMap<String, String> options = new HashMap<String, String>(); + final HashMap<String, String> options = CollectionUtils.newHashMap(); BinaryDictInputOutput.populateOptionsFromFile(raf, headerSize, options); final String version = options.get(VERSION_KEY); if (null == version) { @@ -404,7 +403,7 @@ class BinaryDictionaryGetter { final DictPackSettings dictPackSettings = new DictPackSettings(context); boolean foundMainDict = false; - final ArrayList<AssetFileAddress> fileList = new ArrayList<AssetFileAddress>(); + final ArrayList<AssetFileAddress> fileList = CollectionUtils.newArrayList(); // cachedWordLists may not be null, see doc for getCachedDictionaryList for (final File f : cachedWordLists) { final String wordListId = getWordListIdFromFileName(f.getName()); diff --git a/java/src/com/android/inputmethod/latin/CollectionUtils.java b/java/src/com/android/inputmethod/latin/CollectionUtils.java new file mode 100644 index 000000000..baa2ee1cd --- /dev/null +++ b/java/src/com/android/inputmethod/latin/CollectionUtils.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2012 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.util.SparseArray; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +public class CollectionUtils { + private CollectionUtils() { + // This utility class is not publicly instantiable. + } + + public static <K,V> HashMap<K,V> newHashMap() { + return new HashMap<K,V>(); + } + + public static <K,V> TreeMap<K,V> newTreeMap() { + return new TreeMap<K,V>(); + } + + public static <K, V> Map<K,V> newSynchronizedTreeMap() { + final TreeMap<K,V> treeMap = newTreeMap(); + return Collections.synchronizedMap(treeMap); + } + + public static <K,V> ConcurrentHashMap<K,V> newConcurrentHashMap() { + return new ConcurrentHashMap<K,V>(); + } + + public static <E> HashSet<E> newHashSet() { + return new HashSet<E>(); + } + + public static <E> TreeSet<E> newTreeSet() { + return new TreeSet<E>(); + } + + public static <E> ArrayList<E> newArrayList() { + return new ArrayList<E>(); + } + + public static <E> ArrayList<E> newArrayList(final int initialCapacity) { + return new ArrayList<E>(initialCapacity); + } + + public static <E> ArrayList<E> newArrayList(final Collection<E> collection) { + return new ArrayList<E>(collection); + } + + public static <E> LinkedList<E> newLinkedList() { + return new LinkedList<E>(); + } + + public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList() { + return new CopyOnWriteArrayList<E>(); + } + + public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList( + final Collection<E> collection) { + return new CopyOnWriteArrayList<E>(collection); + } + + public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList(final E[] array) { + return new CopyOnWriteArrayList<E>(array); + } + + public static <E> SparseArray<E> newSparseArray() { + return new SparseArray<E>(); + } +} diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index ee80f2532..4acab6b05 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -35,22 +35,22 @@ public class DictionaryCollection extends Dictionary { public DictionaryCollection(final String dictType) { super(dictType); - mDictionaries = new CopyOnWriteArrayList<Dictionary>(); + mDictionaries = CollectionUtils.newCopyOnWriteArrayList(); } public DictionaryCollection(final String dictType, Dictionary... dictionaries) { super(dictType); if (null == dictionaries) { - mDictionaries = new CopyOnWriteArrayList<Dictionary>(); + mDictionaries = CollectionUtils.newCopyOnWriteArrayList(); } else { - mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries); + mDictionaries = CollectionUtils.newCopyOnWriteArrayList(dictionaries); mDictionaries.removeAll(Collections.singleton(null)); } } public DictionaryCollection(final String dictType, Collection<Dictionary> dictionaries) { super(dictType); - mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries); + mDictionaries = CollectionUtils.newCopyOnWriteArrayList(dictionaries); mDictionaries.removeAll(Collections.singleton(null)); } @@ -63,7 +63,7 @@ public class DictionaryCollection extends Dictionary { // dictionary and add the rest to it if not null, hence the get(0) ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composer, prevWord, proximityInfo); - if (null == suggestions) suggestions = new ArrayList<SuggestedWordInfo>(); + if (null == suggestions) suggestions = CollectionUtils.newArrayList(); final int length = dictionaries.size(); for (int i = 1; i < length; ++ i) { final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(composer, diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 06a5f4b72..cdd01d0c7 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -53,7 +53,7 @@ public class DictionaryFactory { createBinaryDictionary(context, locale)); } - final LinkedList<Dictionary> dictList = new LinkedList<Dictionary>(); + final LinkedList<Dictionary> dictList = CollectionUtils.newLinkedList(); final ArrayList<AssetFileAddress> assetFileList = BinaryDictionaryGetter.getDictionaryFiles(locale, context); if (null != assetFileList) { diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 016530abb..cdf5247de 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -62,7 +62,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * that filename. */ private static final HashMap<String, DictionaryController> sSharedDictionaryControllers = - new HashMap<String, DictionaryController>(); + CollectionUtils.newHashMap(); /** The application context. */ protected final Context mContext; @@ -159,9 +159,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * the native side. */ public void clearFusionDictionary() { + final HashMap<String, String> attributes = CollectionUtils.newHashMap(); mFusionDictionary = new FusionDictionary(new Node(), - new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false, - false)); + new FusionDictionary.DictionaryOptions(attributes, false, false)); } /** @@ -175,7 +175,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { mFusionDictionary.add(word, frequency, null); } else { // TODO: Do this in the subclass, with this class taking an arraylist. - final ArrayList<WeightedString> shortcutTargets = new ArrayList<WeightedString>(); + final ArrayList<WeightedString> shortcutTargets = CollectionUtils.newArrayList(); shortcutTargets.add(new WeightedString(shortcutTarget, frequency)); mFusionDictionary.add(word, frequency, shortcutTargets); } diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index 11dca3404..8a38d1e1b 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -230,7 +230,7 @@ public class ExpandableDictionary extends Dictionary { childNode.mTerminal = true; if (isShortcutOnly) { if (null == childNode.mShortcutTargets) { - childNode.mShortcutTargets = new ArrayList<char[]>(); + childNode.mShortcutTargets = CollectionUtils.newArrayList(); } childNode.mShortcutTargets.add(shortcutTarget.toCharArray()); } else { @@ -250,7 +250,7 @@ public class ExpandableDictionary extends Dictionary { public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, final CharSequence prevWord, final ProximityInfo proximityInfo) { if (reloadDictionaryIfRequired()) return null; - if (composer.size() <= 1) { + if (composer.size() > 1) { if (composer.size() >= BinaryDictionary.MAX_WORD_LENGTH) { return null; } @@ -259,7 +259,7 @@ public class ExpandableDictionary extends Dictionary { return suggestions; } else { if (TextUtils.isEmpty(prevWord)) return null; - final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>(); + final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); runBigramReverseLookUp(prevWord, suggestions); return suggestions; } @@ -278,7 +278,7 @@ public class ExpandableDictionary extends Dictionary { protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes, final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) { - final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>(); + final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); mInputLength = codes.size(); if (mCodes.length < mInputLength) mCodes = new int[mInputLength][]; final InputPointers ips = codes.getInputPointers(); @@ -550,7 +550,7 @@ public class ExpandableDictionary extends Dictionary { Node secondWord = searchWord(mRoots, word2, 0, null); LinkedList<NextWord> bigrams = firstWord.mNGrams; if (bigrams == null || bigrams.size() == 0) { - firstWord.mNGrams = new LinkedList<NextWord>(); + firstWord.mNGrams = CollectionUtils.newLinkedList(); bigrams = firstWord.mNGrams; } else { for (NextWord nw : bigrams) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index f2ebc9776..4d5f93b8f 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -361,7 +361,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mPrefs = prefs; LatinImeLogger.init(this, prefs); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().init(this, prefs, mKeyboardSwitcher); + ResearchLogger.getInstance().init(this, prefs); } InputMethodManagerCompatWrapper.init(this); SubtypeSwitcher.init(this); @@ -381,18 +381,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ImfUtils.setAdditionalInputMethodSubtypes(this, mCurrentSettings.getAdditionalSubtypes()); - Utils.GCUtils.getInstance().reset(); - boolean tryGC = true; - // Shouldn't this be removed? I think that from Honeycomb on, the GC is now actually working - // as expected and this code is useless. - for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { - try { - initSuggest(); - tryGC = false; - } catch (OutOfMemoryError e) { - tryGC = Utils.GCUtils.getInstance().tryGCOrWait("InitSuggest", e); - } - } + initSuggest(); mDisplayOrientation = res.getConfiguration().orientation; @@ -416,7 +405,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // Has to be package-visible for unit tests - /* package */ void loadSettings() { + /* package for test */ + void loadSettings() { // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() // is not guaranteed. It may even be called at the same time on a different thread. if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this); @@ -540,7 +530,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onConfigurationChanged(Configuration conf) { - mSubtypeSwitcher.onConfigurationChanged(conf); + // System locale has been changed. Needs to reload keyboard. + if (mSubtypeSwitcher.onConfigurationChanged(conf, this)) { + loadKeyboard(); + } // If orientation changed while predicting, commit the change if (mDisplayOrientation != conf.orientation) { mDisplayOrientation = conf.orientation; @@ -607,6 +600,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() // is not guaranteed. It may even be called at the same time on a different thread. mSubtypeSwitcher.updateSubtype(subtype); + loadKeyboard(); } private void onStartInputInternal(EditorInfo editorInfo, boolean restarting) { @@ -675,7 +669,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final boolean inputTypeChanged = !mCurrentSettings.isSameInputType(editorInfo); final boolean isDifferentTextField = !restarting || inputTypeChanged; if (isDifferentTextField) { - mSubtypeSwitcher.updateParametersOnStartInputView(); + final boolean currentSubtypeEnabled = mSubtypeSwitcher + .updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled(); + if (!currentSubtypeEnabled) { + // Current subtype is disabled. Needs to update subtype and keyboard. + final InputMethodSubtype newSubtype = ImfUtils.getCurrentInputMethodSubtype( + this, mSubtypeSwitcher.getNoLanguageSubtype()); + mSubtypeSwitcher.updateSubtype(newSubtype); + loadKeyboard(); + } } // The EditorInfo might have a flag that affects fullscreen mode. @@ -907,13 +909,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); - } if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return; mApplicationSpecifiedCompletions = applicationSpecifiedCompletions; if (applicationSpecifiedCompletions == null) { clearSuggestionStrip(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onDisplayCompletions(null); + } return; } @@ -935,6 +937,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // this case? This says to keep whatever the user typed. mWordComposer.setAutoCorrection(mWordComposer.getTypedWord()); setSuggestionStripShown(true); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); + } } private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) { @@ -1055,9 +1060,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CharSequence typedWord = mWordComposer.getTypedWord(); if (typedWord.length() > 0) { mConnection.commitText(typedWord, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(typedWord); - } final CharSequence prevWord = addToUserHistoryDictionary(typedWord); mLastComposedWord = mWordComposer.commitWord( LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(), @@ -1111,12 +1113,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Keyboard.CODE_SPACE) { mConnection.deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } mConnection.commitText(lastTwo.charAt(1) + " ", 1); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit(); + ResearchLogger.latinIME_swapSwapperAndSpace(); } mKeyboardSwitcher.updateShiftState(); } @@ -1133,9 +1132,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelDoubleSpacesTimer(); mConnection.deleteSurroundingText(2, 0); mConnection.commitText(". ", 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_doubleSpaceAutoPeriod(); - } mKeyboardSwitcher.updateShiftState(); return true; } @@ -1199,9 +1195,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void performEditorAction(int actionId) { mConnection.performEditorAction(actionId); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_performEditorAction(actionId); - } } private void handleLanguageSwitchKey() { @@ -1238,6 +1231,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}. if (code >= '0' && code <= '9') { super.sendKeyChar((char)code); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_sendKeyCodePoint(code); + } return; } @@ -1254,9 +1250,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String text = new String(new int[] { code }, 0, 1); mConnection.commitText(text, text.length()); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_sendKeyCodePoint(code); - } } // Implementation of {@link KeyboardActionListener}. @@ -1305,7 +1298,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen onSettingsKeyPressed(); break; case Keyboard.CODE_SHORTCUT: - mSubtypeSwitcher.switchToShortcutIME(); + mSubtypeSwitcher.switchToShortcutIME(this); break; case Keyboard.CODE_ACTION_ENTER: performEditorAction(getActionId(switcher.getKeyboard())); @@ -1367,9 +1360,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendKeyCodePoint(Keyboard.CODE_SPACE); } mConnection.commitText(text, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(text); - } mConnection.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT); @@ -1467,9 +1457,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // like the smiley key or the .com key. final int length = mEnteredText.length(); mConnection.deleteSurroundingText(length, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(length); - } // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false. // In addition we know that spaceState is false, and that we should not be // reverting any autocorrect at this point. So we can safely return. @@ -1489,9 +1476,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateSuggestionStrip(); } else { mConnection.deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } else { if (mLastComposedWord.canRevertCommit()) { @@ -1520,9 +1504,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart; mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd); mConnection.deleteSurroundingText(lengthToDelete, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete); - } } else { // There is no selection, just delete one character. if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) { @@ -1541,14 +1522,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { mConnection.deleteSurroundingText(1, 0); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } if (mDeleteCount > DELETE_ACCELERATE_AT) { mConnection.deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { @@ -1727,7 +1702,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: make this private // Outside LatinIME, only used by the test suite. - /* package for tests */ boolean isShowingPunctuationList() { + /* package for tests */ + boolean isShowingPunctuationList() { if (mSuggestionStripView == null) return false; return mCurrentSettings.mSuggestPuncList == mSuggestionStripView.getSuggestions(); } @@ -1873,10 +1849,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "is empty? Impossible! I must commit suicide."); } Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitCurrentAutoCorrection(typedWord, - autoCorrection.toString()); - } mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separatorCodePoint); @@ -1901,12 +1873,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // So, LatinImeLogger logs "" as a user's input. LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords); // Rely on onCodeInput to do the complicated swapping/stripping logic consistently. - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_punctuationSuggestion(index, suggestion); - } final int primaryCode = suggestion.charAt(0); onCodeInput(primaryCode, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_punctuationSuggestion(index, suggestion); + } return; } @@ -1933,10 +1905,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; mConnection.commitCompletion(completionInfo); mConnection.endBatchEdit(); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickApplicationSpecifiedCompletion( - index, completionInfo.getText()); - } return; } @@ -1945,12 +1913,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String replacedWord = mWordComposer.getTypedWord().toString(); LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion.toString(), index, suggestedWords); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion); - } mExpectingUpdateSelection = true; commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion); + } mConnection.endBatchEdit(); // Don't allow cancellation of manual pick mLastComposedWord.deactivate(); @@ -1985,9 +1953,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions(); mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(chosenWord); - } // Add the word to the user history dictionary final CharSequence prevWord = addToUserHistoryDictionary(chosenWord); // TODO: figure out here if this is an auto-correct or if the best word is actually @@ -2055,9 +2020,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard()); final int length = word.length(); mConnection.deleteSurroundingText(length, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(length); - } mConnection.setComposingText(word, 1); mHandler.postUpdateSuggestionStrip(); } @@ -2085,9 +2047,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } mConnection.deleteSurroundingText(deleteLength, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(deleteLength); - } if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) { mUserHistoryDictionary.cancelAddingUserHistory( previousWord.toString(), committedWord.toString()); @@ -2112,9 +2071,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return mCurrentSettings.isWordSeparator(code); } - // Notify that language or mode have been changed and toggleLanguage will update KeyboardID - // according to new language or mode. Called from SubtypeSwitcher. - public void onRefreshKeyboard() { + // TODO: Make this private + // Outside LatinIME, only used by the {@link InputTestsBase} test suite. + /* package for test */ + void loadKeyboard() { // When the device locale is changed in SetupWizard etc., this method may get called via // onConfigurationChanged before SoftInputWindow is shown. initSuggest(); diff --git a/java/src/com/android/inputmethod/latin/LocaleUtils.java b/java/src/com/android/inputmethod/latin/LocaleUtils.java index b938dd336..3b08cab01 100644 --- a/java/src/com/android/inputmethod/latin/LocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/LocaleUtils.java @@ -193,7 +193,7 @@ public class LocaleUtils { } } - private static final HashMap<String, Locale> sLocaleCache = new HashMap<String, Locale>(); + private static final HashMap<String, Locale> sLocaleCache = CollectionUtils.newHashMap(); /** * Creates a locale from a string specification. diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 8b4c17322..41e59e92d 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -55,7 +55,9 @@ public class RichInputConnection { public void beginBatchEdit() { if (++mNestLevel == 1) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) mIC.beginBatchEdit(); + if (null != mIC) { + mIC.beginBatchEdit(); + } } else { if (DBG) { throw new RuntimeException("Nest level too deep"); @@ -66,7 +68,9 @@ public class RichInputConnection { } public void endBatchEdit() { if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead - if (--mNestLevel == 0 && null != mIC) mIC.endBatchEdit(); + if (--mNestLevel == 0 && null != mIC) { + mIC.endBatchEdit(); + } } private void checkBatchEdit() { @@ -79,12 +83,22 @@ public class RichInputConnection { public void finishComposingText() { checkBatchEdit(); - if (null != mIC) mIC.finishComposingText(); + if (null != mIC) { + mIC.finishComposingText(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_finishComposingText(); + } + } } public void commitText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) mIC.commitText(text, i); + if (null != mIC) { + mIC.commitText(text, i); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitText(text, i); + } + } } public int getCursorCapsMode(final int inputType) { @@ -107,37 +121,72 @@ public class RichInputConnection { public void deleteSurroundingText(final int i, final int j) { checkBatchEdit(); - if (null != mIC) mIC.deleteSurroundingText(i, j); + if (null != mIC) { + mIC.deleteSurroundingText(i, j); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_deleteSurroundingText(i, j); + } + } } public void performEditorAction(final int actionId) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) mIC.performEditorAction(actionId); + if (null != mIC) { + mIC.performEditorAction(actionId); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_performEditorAction(actionId); + } + } } public void sendKeyEvent(final KeyEvent keyEvent) { checkBatchEdit(); - if (null != mIC) mIC.sendKeyEvent(keyEvent); + if (null != mIC) { + mIC.sendKeyEvent(keyEvent); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_sendKeyEvent(keyEvent); + } + } } public void setComposingText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) mIC.setComposingText(text, i); + if (null != mIC) { + mIC.setComposingText(text, i); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_setComposingText(text, i); + } + } } public void setSelection(final int from, final int to) { checkBatchEdit(); - if (null != mIC) mIC.setSelection(from, to); + if (null != mIC) { + mIC.setSelection(from, to); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_setSelection(from, to); + } + } } public void commitCorrection(final CorrectionInfo correctionInfo) { checkBatchEdit(); - if (null != mIC) mIC.commitCorrection(correctionInfo); + if (null != mIC) { + mIC.commitCorrection(correctionInfo); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitCorrection(correctionInfo); + } + } } public void commitCompletion(final CompletionInfo completionInfo) { checkBatchEdit(); - if (null != mIC) mIC.commitCompletion(completionInfo); + if (null != mIC) { + mIC.commitCompletion(completionInfo); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitCompletion(completionInfo); + } + } } public CharSequence getNthPreviousWord(final String sentenceSeperators, final int n) { @@ -315,9 +364,6 @@ public class RichInputConnection { if (lastOne != null && lastOne.length() == 1 && lastOne.charAt(0) == Keyboard.CODE_SPACE) { deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } @@ -382,13 +428,7 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } commitText(" ", 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit(); - } return true; } @@ -409,13 +449,7 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } commitText(" " + textBeforeCursor.subSequence(0, 1), 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertSwapPunctuation(); - } return true; } } diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index c8755be9d..dcd2532c1 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -185,7 +185,7 @@ public class SettingsValues { // Helper functions to create member values. private static SuggestedWords createSuggestPuncList(final String[] puncs) { - final ArrayList<SuggestedWordInfo> puncList = new ArrayList<SuggestedWordInfo>(); + final ArrayList<SuggestedWordInfo> puncList = CollectionUtils.newArrayList(); if (puncs != null) { for (final String puncSpec : puncs) { puncList.add(new SuggestedWordInfo(KeySpecParser.getLabel(puncSpec), diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java index 6e7d985d6..39c59b44c 100644 --- a/java/src/com/android/inputmethod/latin/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/StringUtils.java @@ -53,7 +53,7 @@ public class StringUtils { if (TextUtils.isEmpty(csv)) return ""; final String[] elements = csv.split(","); if (!containsInArray(key, elements)) return csv; - final ArrayList<String> result = new ArrayList<String>(elements.length - 1); + final ArrayList<String> result = CollectionUtils.newArrayList(elements.length - 1); for (final String element : elements) { if (!key.equals(element)) result.add(element); } diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java index 21c9c0d1e..de5f515b0 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java +++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java @@ -45,13 +45,13 @@ public class SubtypeLocale { private static String[] sPredefinedKeyboardLayoutSet; // Keyboard layout to its display name map. private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap = - new HashMap<String, String>(); + CollectionUtils.newHashMap(); // Keyboard layout to subtype name resource id map. private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap = - new HashMap<String, Integer>(); + CollectionUtils.newHashMap(); // Exceptional locale to subtype name resource id map. private static final HashMap<String, Integer> sExceptionalLocaleToWithLayoutNameIdsMap = - new HashMap<String, Integer>(); + CollectionUtils.newHashMap(); private static final String SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX = "string/subtype_generic_"; private static final String SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX = @@ -60,11 +60,11 @@ public class SubtypeLocale { "string/subtype_no_language_"; // Exceptional locales to display name map. private static final HashMap<String, String> sExceptionalDisplayNamesMap = - new HashMap<String, String>(); + CollectionUtils.newHashMap(); // Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value. // This is for compatibility to keep the same subtype ids as pre-JellyBean. private static final HashMap<String,String> sLocaleAndExtraValueToKeyboardLayoutSetMap = - new HashMap<String,String>(); + CollectionUtils.newHashMap(); private SubtypeLocale() { // Intentional empty constructor for utility class. diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index a7a5fcb5f..c693edcca 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; +import android.inputmethodservice.InputMethodService; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.AsyncTask; @@ -42,7 +43,6 @@ public class SubtypeSwitcher { private static final String TAG = SubtypeSwitcher.class.getSimpleName(); private static final SubtypeSwitcher sInstance = new SubtypeSwitcher(); - private /* final */ LatinIME mService; private /* final */ InputMethodManager mImm; private /* final */ Resources mResources; private /* final */ ConnectivityManager mConnectivityManager; @@ -68,11 +68,11 @@ public class SubtypeSwitcher { return mEnabledSubtypeCount >= 2 || !mIsSystemLanguageSameAsInputLanguage; } - public void updateEnabledSubtypeCount(int count) { + public void updateEnabledSubtypeCount(final int count) { mEnabledSubtypeCount = count; } - public void updateIsSystemLanguageSameAsInputLanguage(boolean isSame) { + public void updateIsSystemLanguageSameAsInputLanguage(final boolean isSame) { mIsSystemLanguageSameAsInputLanguage = isSame; } } @@ -81,18 +81,17 @@ public class SubtypeSwitcher { return sInstance; } - public static void init(LatinIME service) { - SubtypeLocale.init(service); - sInstance.initialize(service); - sInstance.updateAllParameters(); + public static void init(final Context context) { + SubtypeLocale.init(context); + sInstance.initialize(context); + sInstance.updateAllParameters(context); } private SubtypeSwitcher() { // Intentional empty constructor for singleton. } - private void initialize(LatinIME service) { - mService = service; + private void initialize(final Context service) { mResources = service.getResources(); mImm = ImfUtils.getInputMethodManager(service); mConnectivityManager = (ConnectivityManager) service.getSystemService( @@ -111,39 +110,46 @@ public class SubtypeSwitcher { // Update all parameters stored in SubtypeSwitcher. // Only configuration changed event is allowed to call this because this is heavy. - private void updateAllParameters() { + private void updateAllParameters(final Context context) { mCurrentSystemLocale = mResources.getConfiguration().locale; - updateSubtype(ImfUtils.getCurrentInputMethodSubtype(mService, mNoLanguageSubtype)); - updateParametersOnStartInputView(); + updateSubtype(ImfUtils.getCurrentInputMethodSubtype(context, mNoLanguageSubtype)); + updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled(); } - // Update parameters which are changed outside LatinIME. This parameters affect UI so they - // should be updated every time onStartInputview. - public void updateParametersOnStartInputView() { - updateEnabledSubtypes(); + /** + * Update parameters which are changed outside LatinIME. This parameters affect UI so they + * should be updated every time onStartInputView. + * + * @return true if the current subtype is enabled. + */ + public boolean updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled() { + final boolean currentSubtypeEnabled = + updateEnabledSubtypesAndReturnIfEnabled(mCurrentSubtype); updateShortcutIME(); + return currentSubtypeEnabled; } - // Reload enabledSubtypes from the framework. - private void updateEnabledSubtypes() { - final InputMethodSubtype currentSubtype = mCurrentSubtype; - boolean foundCurrentSubtypeBecameDisabled = true; + /** + * Update enabled subtypes from the framework. + * + * @param subtype the subtype to be checked + * @return true if the {@code subtype} is enabled. + */ + private boolean updateEnabledSubtypesAndReturnIfEnabled(final InputMethodSubtype subtype) { final List<InputMethodSubtype> enabledSubtypesOfThisIme = mImm.getEnabledInputMethodSubtypeList(null, true); - for (InputMethodSubtype ims : enabledSubtypesOfThisIme) { - if (ims.equals(currentSubtype)) { - foundCurrentSubtypeBecameDisabled = false; - } - } mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size()); - if (foundCurrentSubtypeBecameDisabled) { - if (DBG) { - Log.w(TAG, "Last subtype: " - + currentSubtype.getLocale() + "/" + currentSubtype.getExtraValue()); - Log.w(TAG, "Last subtype was disabled. Update to the current one."); + + for (final InputMethodSubtype ims : enabledSubtypesOfThisIme) { + if (ims.equals(subtype)) { + return true; } - updateSubtype(ImfUtils.getCurrentInputMethodSubtype(mService, mNoLanguageSubtype)); } + if (DBG) { + Log.w(TAG, "Subtype: " + subtype.getLocale() + "/" + subtype.getExtraValue() + + " was disabled"); + } + return false; } private void updateShortcutIME() { @@ -159,8 +165,8 @@ public class SubtypeSwitcher { mImm.getShortcutInputMethodsAndSubtypes(); mShortcutInputMethodInfo = null; mShortcutSubtype = null; - for (InputMethodInfo imi : shortcuts.keySet()) { - List<InputMethodSubtype> subtypes = shortcuts.get(imi); + for (final InputMethodInfo imi : shortcuts.keySet()) { + final List<InputMethodSubtype> subtypes = shortcuts.get(imi); // TODO: Returns the first found IMI for now. Should handle all shortcuts as // appropriate. mShortcutInputMethodInfo = imi; @@ -194,24 +200,24 @@ public class SubtypeSwitcher { mCurrentSubtype = newSubtype; updateShortcutIME(); - mService.onRefreshKeyboard(); } //////////////////////////// // Shortcut IME functions // //////////////////////////// - public void switchToShortcutIME() { + public void switchToShortcutIME(final InputMethodService context) { if (mShortcutInputMethodInfo == null) { return; } final String imiId = mShortcutInputMethodInfo.getId(); - switchToTargetIME(imiId, mShortcutSubtype); + switchToTargetIME(imiId, mShortcutSubtype, context); } - private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype) { - final IBinder token = mService.getWindow().getWindow().getAttributes().token; + private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype, + final InputMethodService context) { + final IBinder token = context.getWindow().getWindow().getAttributes().token; if (token == null) { return; } @@ -253,7 +259,7 @@ public class SubtypeSwitcher { return true; } - public void onNetworkStateChanged(Intent intent) { + public void onNetworkStateChanged(final Intent intent) { final boolean noConnection = intent.getBooleanExtra( ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); mIsNetworkConnected = !noConnection; @@ -265,7 +271,7 @@ public class SubtypeSwitcher { // Subtype Switching functions // ////////////////////////////////// - public boolean needsToDisplayLanguage(Locale keyboardLocale) { + public boolean needsToDisplayLanguage(final Locale keyboardLocale) { if (keyboardLocale.toString().equals(SubtypeLocale.NO_LANGUAGE)) { return true; } @@ -279,12 +285,14 @@ public class SubtypeSwitcher { return SubtypeLocale.getSubtypeLocale(mCurrentSubtype); } - public void onConfigurationChanged(Configuration conf) { + public boolean onConfigurationChanged(final Configuration conf, final Context context) { final Locale systemLocale = conf.locale; + final boolean systemLocaleChanged = !systemLocale.equals(mCurrentSystemLocale); // If system configuration was changed, update all parameters. - if (!systemLocale.equals(mCurrentSystemLocale)) { - updateAllParameters(); + if (systemLocaleChanged) { + updateAllParameters(context); } + return systemLocaleChanged; } public InputMethodSubtype getCurrentSubtype() { diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index c75322691..51ed09604 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -51,7 +51,7 @@ public class Suggest { private Dictionary mMainDictionary; private ContactsBinaryDictionary mContactsDict; private final ConcurrentHashMap<String, Dictionary> mDictionaries = - new ConcurrentHashMap<String, Dictionary>(); + CollectionUtils.newConcurrentHashMap(); public static final int MAX_SUGGESTIONS = 18; @@ -242,7 +242,7 @@ public class Suggest { } final ArrayList<SuggestedWordInfo> suggestionsContainer = - new ArrayList<SuggestedWordInfo>(suggestionsSet); + CollectionUtils.newArrayList(suggestionsSet); final int suggestionsCount = suggestionsContainer.size(); final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); final boolean isAllUpperCase = wordComposer.isAllUpperCase(); @@ -307,7 +307,7 @@ public class Suggest { } final ArrayList<SuggestedWordInfo> suggestionsContainer = - new ArrayList<SuggestedWordInfo>(suggestionsSet); + CollectionUtils.newArrayList(suggestionsSet); final int suggestionsCount = suggestionsContainer.size(); final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock(); final boolean isAllUpperCase = wordComposer.isAllUpperCase(); @@ -338,7 +338,7 @@ public class Suggest { typedWordInfo.setDebugString("+"); final int suggestionsSize = suggestions.size(); final ArrayList<SuggestedWordInfo> suggestionsList = - new ArrayList<SuggestedWordInfo>(suggestionsSize); + CollectionUtils.newArrayList(suggestionsSize); suggestionsList.add(typedWordInfo); // Note: i here is the index in mScores[], but the index in mSuggestions is one more // than i because we added the typed word to mSuggestions without touching mScores. @@ -391,7 +391,7 @@ public class Suggest { } public void close() { - final HashSet<Dictionary> dictionaries = new HashSet<Dictionary>(); + final HashSet<Dictionary> dictionaries = CollectionUtils.newHashSet(); dictionaries.addAll(mDictionaries.values()); for (final Dictionary dictionary : dictionaries) { dictionary.close(); diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 88fc006df..68ecfa0d7 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -24,8 +24,10 @@ import java.util.Arrays; import java.util.HashSet; public class SuggestedWords { + private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = + CollectionUtils.newArrayList(0); public static final SuggestedWords EMPTY = new SuggestedWords( - new ArrayList<SuggestedWordInfo>(0), false, false, false, false, false); + EMPTY_WORD_INFO_LIST, false, false, false, false, false); public final boolean mTypedWordValid; // Note: this INCLUDES cases where the word will auto-correct to itself. A good definition @@ -83,7 +85,7 @@ public class SuggestedWords { public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions( final CompletionInfo[] infos) { - final ArrayList<SuggestedWordInfo> result = new ArrayList<SuggestedWordInfo>(); + final ArrayList<SuggestedWordInfo> result = CollectionUtils.newArrayList(); for (CompletionInfo info : infos) { if (null != info && info.getText() != null) { result.add(new SuggestedWordInfo(info.getText(), SuggestedWordInfo.MAX_SCORE, @@ -97,8 +99,8 @@ public class SuggestedWords { // and replace it with what the user currently typed. public static ArrayList<SuggestedWordInfo> getTypedWordAndPreviousSuggestions( final CharSequence typedWord, final SuggestedWords previousSuggestions) { - final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<SuggestedWordInfo>(); - final HashSet<String> alreadySeen = new HashSet<String>(); + final ArrayList<SuggestedWordInfo> suggestionsList = CollectionUtils.newArrayList(); + final HashSet<String> alreadySeen = CollectionUtils.newHashSet(); suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED, Dictionary.TYPE_USER_TYPED)); alreadySeen.add(typedWord.toString()); diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java index d516e72ad..6c9d1c250 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java @@ -93,10 +93,10 @@ public class UserHistoryDictionary extends ExpandableDictionary { private final static HashMap<String, String> sDictProjectionMap; private final static ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>> - sLangDictCache = new ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>(); + sLangDictCache = CollectionUtils.newConcurrentHashMap(); static { - sDictProjectionMap = new HashMap<String, String>(); + sDictProjectionMap = CollectionUtils.newHashMap(); sDictProjectionMap.put(MAIN_COLUMN_ID, MAIN_COLUMN_ID); sDictProjectionMap.put(MAIN_COLUMN_WORD1, MAIN_COLUMN_WORD1); sDictProjectionMap.put(MAIN_COLUMN_WORD2, MAIN_COLUMN_WORD2); diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java index 610652ac1..bb0f5429a 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java @@ -29,9 +29,8 @@ import java.util.Set; public class UserHistoryDictionaryBigramList { public static final byte FORGETTING_CURVE_INITIAL_VALUE = 0; private static final String TAG = UserHistoryDictionaryBigramList.class.getSimpleName(); - private static final HashMap<String, Byte> EMPTY_BIGRAM_MAP = new HashMap<String, Byte>(); - private final HashMap<String, HashMap<String, Byte>> mBigramMap = - new HashMap<String, HashMap<String, Byte>>(); + private static final HashMap<String, Byte> EMPTY_BIGRAM_MAP = CollectionUtils.newHashMap(); + private final HashMap<String, HashMap<String, Byte>> mBigramMap = CollectionUtils.newHashMap(); private int mSize = 0; public void evictAll() { @@ -57,7 +56,7 @@ public class UserHistoryDictionaryBigramList { if (mBigramMap.containsKey(word1)) { map = mBigramMap.get(word1); } else { - map = new HashMap<String, Byte>(); + map = CollectionUtils.newHashMap(); mBigramMap.put(word1, map); } if (!map.containsKey(word2)) { diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index c6b5c338b..fc7a42100 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -65,44 +65,6 @@ public class Utils { } } - public static class GCUtils { - private static final String GC_TAG = GCUtils.class.getSimpleName(); - public static final int GC_TRY_COUNT = 2; - // GC_TRY_LOOP_MAX is used for the hard limit of GC wait, - // GC_TRY_LOOP_MAX should be greater than GC_TRY_COUNT. - public static final int GC_TRY_LOOP_MAX = 5; - private static final long GC_INTERVAL = DateUtils.SECOND_IN_MILLIS; - private static GCUtils sInstance = new GCUtils(); - private int mGCTryCount = 0; - - public static GCUtils getInstance() { - return sInstance; - } - - public void reset() { - mGCTryCount = 0; - } - - public boolean tryGCOrWait(String metaData, Throwable t) { - if (mGCTryCount == 0) { - System.gc(); - } - if (++mGCTryCount > GC_TRY_COUNT) { - LatinImeLogger.logOnException(metaData, t); - return false; - } else { - try { - Thread.sleep(GC_INTERVAL); - return true; - } catch (InterruptedException e) { - Log.e(GC_TAG, "Sleep was interrupted."); - LatinImeLogger.logOnException(metaData, t); - return false; - } - } - } - } - /* package */ static class RingCharBuffer { private static RingCharBuffer sRingCharBuffer = new RingCharBuffer(); private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC'; @@ -477,7 +439,7 @@ public class Utils { private static final String HARDWARE_PREFIX = Build.HARDWARE + ","; private static final HashMap<String, String> sDeviceOverrideValueMap = - new HashMap<String, String>(); + CollectionUtils.newHashMap(); public static String getDeviceOverrideValue(Resources res, int overrideResId, String defValue) { final int orientation = res.getConfiguration().orientation; @@ -495,7 +457,7 @@ public class Utils { return sDeviceOverrideValueMap.get(key); } - private static final HashMap<String, Long> EMPTY_LT_HASH_MAP = new HashMap<String, Long>(); + private static final HashMap<String, Long> EMPTY_LT_HASH_MAP = CollectionUtils.newHashMap(); private static final String LOCALE_AND_TIME_STR_SEPARATER = ","; public static HashMap<String, Long> localeAndTimeStrToHashMap(String str) { if (TextUtils.isEmpty(str)) { @@ -506,7 +468,7 @@ public class Utils { if (N < 2 || N % 2 != 0) { return EMPTY_LT_HASH_MAP; } - final HashMap<String, Long> retval = new HashMap<String, Long>(); + final HashMap<String, Long> retval = CollectionUtils.newHashMap(); for (int i = 0; i < N / 2; ++i) { final String localeStr = ss[i * 2]; final long time = Long.valueOf(ss[i * 2 + 1]); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index d05dc021a..eef7a51f2 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -25,6 +25,7 @@ import android.view.textservice.SuggestionsInfo; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.ContactsBinaryDictionary; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.DictionaryCollection; @@ -62,10 +63,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService public static final int CAPITALIZE_ALL = 2; // All caps private final static String[] EMPTY_STRING_ARRAY = new String[0]; - private Map<String, DictionaryPool> mDictionaryPools = - Collections.synchronizedMap(new TreeMap<String, DictionaryPool>()); + private Map<String, DictionaryPool> mDictionaryPools = CollectionUtils.newSynchronizedTreeMap(); private Map<String, UserBinaryDictionary> mUserDictionaries = - Collections.synchronizedMap(new TreeMap<String, UserBinaryDictionary>()); + CollectionUtils.newSynchronizedTreeMap(); private ContactsBinaryDictionary mContactsDictionary; // The threshold for a candidate to be offered as a suggestion. @@ -77,7 +77,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService private final Object mUseContactsLock = new Object(); private final HashSet<WeakReference<DictionaryCollection>> mDictionaryCollectionsList = - new HashSet<WeakReference<DictionaryCollection>>(); + CollectionUtils.newHashSet(); public static final int SCRIPT_LATIN = 0; public static final int SCRIPT_CYRILLIC = 1; @@ -93,7 +93,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService // proximity to pass to the dictionary descent algorithm. // IMPORTANT: this only contains languages - do not write countries in there. // Only the language is searched from the map. - mLanguageToScript = new TreeMap<String, Integer>(); + mLanguageToScript = CollectionUtils.newTreeMap(); mLanguageToScript.put("en", SCRIPT_LATIN); mLanguageToScript.put("fr", SCRIPT_LATIN); mLanguageToScript.put("de", SCRIPT_LATIN); @@ -231,7 +231,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService mSuggestionThreshold = suggestionThreshold; mRecommendedThreshold = recommendedThreshold; mMaxLength = maxLength; - mSuggestions = new ArrayList<CharSequence>(maxLength + 1); + mSuggestions = CollectionUtils.newArrayList(maxLength + 1); mScores = new int[mMaxLength]; } @@ -359,10 +359,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService private void closeAllDictionaries() { final Map<String, DictionaryPool> oldPools = mDictionaryPools; - mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>()); + mDictionaryPools = CollectionUtils.newSynchronizedTreeMap(); final Map<String, UserBinaryDictionary> oldUserDictionaries = mUserDictionaries; - mUserDictionaries = - Collections.synchronizedMap(new TreeMap<String, UserBinaryDictionary>()); + mUserDictionaries = CollectionUtils.newSynchronizedTreeMap(); new Thread("spellchecker_close_dicts") { @Override public void run() { diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java index 501a0e221..5a1bd37f5 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java @@ -22,6 +22,8 @@ import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; +import com.android.inputmethod.latin.CollectionUtils; + import java.util.ArrayList; public class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession { @@ -40,10 +42,10 @@ public class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSess return null; } final int N = ssi.getSuggestionsCount(); - final ArrayList<Integer> additionalOffsets = new ArrayList<Integer>(); - final ArrayList<Integer> additionalLengths = new ArrayList<Integer>(); + final ArrayList<Integer> additionalOffsets = CollectionUtils.newArrayList(); + final ArrayList<Integer> additionalLengths = CollectionUtils.newArrayList(); final ArrayList<SuggestionsInfo> additionalSuggestionsInfos = - new ArrayList<SuggestionsInfo>(); + CollectionUtils.newArrayList(); String currentWord = null; for (int i = 0; i < N; ++i) { final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java index 218eab7de..53aa6c719 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin.spellcheck; import android.util.Log; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; @@ -45,7 +46,7 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> { private final Locale mLocale; private int mSize; private volatile boolean mClosed; - final static ArrayList<SuggestedWordInfo> noSuggestions = new ArrayList<SuggestedWordInfo>(); + final static ArrayList<SuggestedWordInfo> noSuggestions = CollectionUtils.newArrayList(); private final static DictAndProximity dummyDict = new DictAndProximity( new Dictionary(Dictionary.TYPE_MAIN) { @Override diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java index 1762e29aa..fe5225ebd 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin.spellcheck; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.Constants; import java.util.TreeMap; @@ -59,7 +60,7 @@ public class SpellCheckerProximityInfo { // character. // Since we need to build such an array, we want to be able to search in our big proximity // data quickly by character, and a map is probably the best way to do this. - final private static TreeMap<Integer, Integer> INDICES = new TreeMap<Integer, Integer>(); + final private static TreeMap<Integer, Integer> INDICES = CollectionUtils.newTreeMap(); // The proximity here is the union of // - the proximity for a QWERTY keyboard. @@ -122,7 +123,7 @@ public class SpellCheckerProximityInfo { } private static class Cyrillic { - final private static TreeMap<Integer, Integer> INDICES = new TreeMap<Integer, Integer>(); + final private static TreeMap<Integer, Integer> INDICES = CollectionUtils.newTreeMap(); // TODO: The following table is solely based on the keyboard layout. Consult with Russian // speakers on commonly misspelled words/letters. final static int[] PROXIMITY = { diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index bd9a0da13..afc4293c0 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -58,6 +58,7 @@ import com.android.inputmethod.keyboard.MoreKeysPanel; import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.keyboard.ViewLayoutUtils; import com.android.inputmethod.latin.AutoCorrection; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; @@ -88,9 +89,9 @@ public class SuggestionStripView extends RelativeLayout implements OnClickListen private final MoreSuggestions.Builder mMoreSuggestionsBuilder; private final PopupWindow mMoreSuggestionsWindow; - private final ArrayList<TextView> mWords = new ArrayList<TextView>(); - private final ArrayList<TextView> mInfos = new ArrayList<TextView>(); - private final ArrayList<View> mDividers = new ArrayList<View>(); + private final ArrayList<TextView> mWords = CollectionUtils.newArrayList(); + private final ArrayList<TextView> mInfos = CollectionUtils.newArrayList(); + private final ArrayList<View> mDividers = CollectionUtils.newArrayList(); private final PopupWindow mPreviewPopup; private final TextView mPreviewText; @@ -167,7 +168,7 @@ public class SuggestionStripView extends RelativeLayout implements OnClickListen private final int mSuggestionStripOption; - private final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(); + private final ArrayList<CharSequence> mTexts = CollectionUtils.newArrayList(); public boolean mMoreSuggestionsAvailable; diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 845ba9c4c..09a22eff1 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -34,15 +34,18 @@ import android.graphics.Paint.Style; import android.inputmethodservice.InputMethodService; import android.os.Build; import android.os.IBinder; +import android.os.SystemClock; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.widget.Button; @@ -51,9 +54,9 @@ import android.widget.Toast; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.MainKeyboardView; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.LatinIME; @@ -112,7 +115,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // the system to do so. /* package */ ResearchLog mIntentionalResearchLog; // LogUnits are queued here and released only when the user requests the intentional log. - private List<LogUnit> mIntentionalResearchLogQueue = new ArrayList<LogUnit>(); + private List<LogUnit> mIntentionalResearchLogQueue = CollectionUtils.newArrayList(); private boolean mIsPasswordView = false; private boolean mIsLoggingSuspended = false; @@ -134,20 +137,21 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // used to check whether words are not unique private Suggest mSuggest; private Dictionary mDictionary; - private KeyboardSwitcher mKeyboardSwitcher; + private MainKeyboardView mMainKeyboardView; private InputMethodService mInputMethodService; + private final Statistics mStatistics; private ResearchLogUploader mResearchLogUploader; private ResearchLogger() { + mStatistics = Statistics.getInstance(); } public static ResearchLogger getInstance() { return sInstance; } - public void init(final InputMethodService ims, final SharedPreferences prefs, - KeyboardSwitcher keyboardSwitcher) { + public void init(final InputMethodService ims, final SharedPreferences prefs) { assert ims != null; if (ims == null) { Log.w(TAG, "IMS is null; logging is off"); @@ -179,7 +183,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } mResearchLogUploader = new ResearchLogUploader(ims, mFilesDir); mResearchLogUploader.start(); - mKeyboardSwitcher = keyboardSwitcher; mInputMethodService = ims; mPrefs = prefs; } @@ -193,10 +196,15 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } - public void mainKeyboardView_onAttachedToWindow() { + public void mainKeyboardView_onAttachedToWindow(final MainKeyboardView mainKeyboardView) { + mMainKeyboardView = mainKeyboardView; maybeShowSplashScreen(); } + public void mainKeyboardView_onDetachedFromWindow() { + mMainKeyboardView = null; + } + private boolean hasSeenSplash() { return mPrefs.getBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, false); } @@ -210,7 +218,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (mSplashDialog != null && mSplashDialog.isShowing()) { return; } - final IBinder windowToken = mKeyboardSwitcher.getMainKeyboardView().getWindowToken(); + final IBinder windowToken = mMainKeyboardView != null + ? mMainKeyboardView.getWindowToken() : null; if (windowToken == null) { return; } @@ -269,10 +278,35 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return new File(filesDir, sb.toString()); } + private void checkForEmptyEditor() { + if (mInputMethodService == null) { + return; + } + final InputConnection ic = mInputMethodService.getCurrentInputConnection(); + if (ic == null) { + return; + } + final CharSequence textBefore = ic.getTextBeforeCursor(1, 0); + if (!TextUtils.isEmpty(textBefore)) { + mStatistics.setIsEmptyUponStarting(false); + return; + } + final CharSequence textAfter = ic.getTextAfterCursor(1, 0); + if (!TextUtils.isEmpty(textAfter)) { + mStatistics.setIsEmptyUponStarting(false); + return; + } + if (textBefore != null && textAfter != null) { + mStatistics.setIsEmptyUponStarting(true); + } + } + private void start() { maybeShowSplashScreen(); updateSuspendedState(); requestIndicatorRedraw(); + mStatistics.reset(); + checkForEmptyEditor(); if (!isAllowedToLog()) { // Log.w(TAG, "not in usability mode; not logging"); return; @@ -296,6 +330,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } /* package */ void stop() { + logStatistics(); + publishLogUnit(mCurrentLogUnit, true); + mCurrentLogUnit = new LogUnit(); + if (mMainResearchLog != null) { mMainResearchLog.stop(); } @@ -304,6 +342,26 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } + private static final String[] EVENTKEYS_STATISTICS = { + "Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount", + "wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys", + "averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete" + }; + private static void logStatistics() { + final ResearchLogger researchLogger = getInstance(); + final Statistics statistics = researchLogger.mStatistics; + final Object[] values = { + statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount, + statistics.mSpaceCount, statistics.mDeleteKeyCount, + statistics.mWordCount, statistics.mIsEmptyUponStarting, + statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(), + statistics.mBeforeDeleteKeyCounter.getAverageTime(), + statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), + statistics.mAfterDeleteKeyCounter.getAverageTime() + }; + researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values); + } + private void setLoggingAllowed(boolean enableLogging) { if (mPrefs == null) { return; @@ -460,7 +518,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private void saveLogsForFeedback() { mFeedbackLog = mIntentionalResearchLog; if (mIntentionalResearchLogQueue != null) { - mFeedbackQueue = new ArrayList<LogUnit>(mIntentionalResearchLogQueue); + mFeedbackQueue = CollectionUtils.newArrayList(mIntentionalResearchLogQueue); } else { mFeedbackQueue = null; } @@ -470,7 +528,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mMainResearchLog = null; mIntentionalResearchLog = null; - mIntentionalResearchLogQueue = new ArrayList<LogUnit>(); + mIntentionalResearchLogQueue = CollectionUtils.newArrayList(); } private static final int LOG_DRAIN_TIMEOUT_IN_MS = 1000 * 5; @@ -538,14 +596,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (!IS_SHOWING_INDICATOR) { return; } - if (mKeyboardSwitcher == null) { - return; - } - final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); - if (mainKeyboardView == null) { + if (mMainKeyboardView == null) { return; } - mainKeyboardView.invalidateAllKeys(); + mMainKeyboardView.invalidateAllKeys(); } @@ -704,6 +758,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mLoggingFrequencyState.onWordLogged(); } mCurrentLogUnit = new LogUnit(); + mStatistics.recordWordEntered(); } private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) { @@ -726,9 +781,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } static class LogUnit { - private final List<String[]> mKeysList = new ArrayList<String[]>(); - private final List<Object[]> mValuesList = new ArrayList<Object[]>(); - private final List<Boolean> mIsPotentiallyPrivate = new ArrayList<Boolean>(); + private final List<String[]> mKeysList = CollectionUtils.newArrayList(); + private final List<Object[]> mValuesList = CollectionUtils.newArrayList(); + private final List<Boolean> mIsPotentiallyPrivate = CollectionUtils.newArrayList(); private void addLogAtom(final String[] keys, final Object[] values, final Boolean isPotentiallyPrivate) { @@ -847,20 +902,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang stop(); } - private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = { - "LatinIMECommitText", "typedWord" - }; - - public static void latinIME_commitText(final CharSequence typedWord) { - final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); - final Object[] values = { - scrubbedWord - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values); - researchLogger.onWordComplete(scrubbedWord); - } - private static final String[] EVENTKEYS_USER_FEEDBACK = { "UserFeedback", "FeedbackContents" }; @@ -912,48 +953,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); - } - - private static final String[] EVENTKEYS_CORRECTION = { - "LogCorrection", "subgroup", "before", "after", "position" - }; - public static void logCorrection(final String subgroup, final String before, final String after, - final int position) { - final Object[] values = { - subgroup, scrubDigitsFromString(before), scrubDigitsFromString(after), position - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_CORRECTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION = { - "LatinIMECommitCurrentAutoCorrection", "typedWord", "autoCorrection" - }; - public static void latinIME_commitCurrentAutoCorrection(final String typedWord, - final String autoCorrection) { - final Object[] values = { - scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) - }; final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = { - "LatinIMEDeleteSurroundingText", "length" - }; - public static void latinIME_deleteSurroundingText(final int length) { - final Object[] values = { - length - }; - getInstance().enqueueEvent(EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT, values); - } - - private static final String[] EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD = { - "LatinIMEDoubleSpaceAutoPeriod" - }; - public static void latinIME_doubleSpaceAutoPeriod() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD, EVENTKEYS_NULLVALUES); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); + researchLogger.mStatistics.recordChar(code, SystemClock.uptimeMillis()); } private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { @@ -980,6 +982,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_onWindowHidden(final int savedSelectionStart, final int savedSelectionEnd, final InputConnection ic) { if (ic != null) { + // Capture the TextView contents. This will trigger onUpdateSelection(), so we + // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called, + // it can tell that it was generated by the logging code, and not by the user, and + // therefore keep user-visible state as is. ic.beginBatchEdit(); ic.performContextMenuAction(android.R.id.selectAll); CharSequence charSequence = ic.getSelectedText(0); @@ -1049,29 +1055,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONUPDATESELECTION, values); } - private static final String[] EVENTKEYS_LATINIME_PERFORMEDITORACTION = { - "LatinIMEPerformEditorAction", "imeActionNext" - }; - public static void latinIME_performEditorAction(final int imeActionNext) { - final Object[] values = { - imeActionNext - }; - getInstance().enqueueEvent(EVENTKEYS_LATINIME_PERFORMEDITORACTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION = { - "LatinIMEPickApplicationSpecifiedCompletion", "index", "text", "x", "y" - }; - public static void latinIME_pickApplicationSpecifiedCompletion(final int index, - final CharSequence cs) { - final Object[] values = { - index, cs, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, values); - } - private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = { "LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y" }; @@ -1099,21 +1082,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values); } - private static final String[] EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT = { - "LatinIMERevertDoubleSpaceWhileInBatchEdit" - }; - public static void latinIME_revertDoubleSpaceWhileInBatchEdit() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT, - EVENTKEYS_NULLVALUES); - } - - private static final String[] EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION = { - "LatinIMERevertSwapPunctuation" - }; - public static void latinIME_revertSwapPunctuation() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION, EVENTKEYS_NULLVALUES); - } - private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = { "LatinIMESendKeyCodePoint", "code" }; @@ -1124,12 +1092,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); } - private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT = { - "LatinIMESwapSwapperAndSpaceWhileInBatchEdit" + private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE = { + "LatinIMESwapSwapperAndSpace" }; - public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT, - EVENTKEYS_NULLVALUES); + public static void latinIME_swapSwapperAndSpace() { + getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE, EVENTKEYS_NULLVALUES); } private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS = { @@ -1248,6 +1215,109 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONMOVEEVENT, values); } + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION = { + "RichInputConnectionCommitCompletion", "completionInfo" + }; + public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) { + final Object[] values = { + completionInfo + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION = { + "RichInputConnectionCommitCorrection", "CorrectionInfo" + }; + public static void richInputConnection_commitCorrection(CorrectionInfo correctionInfo) { + final String typedWord = correctionInfo.getOldText().toString(); + final String autoCorrection = correctionInfo.getNewText().toString(); + final Object[] values = { + scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT = { + "RichInputConnectionCommitText", "typedWord", "newCursorPosition" + }; + public static void richInputConnection_commitText(final CharSequence typedWord, + final int newCursorPosition) { + final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); + final Object[] values = { + scrubbedWord, newCursorPosition + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT, + values); + researchLogger.onWordComplete(scrubbedWord); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT = { + "RichInputConnectionDeleteSurroundingText", "beforeLength", "afterLength" + }; + public static void richInputConnection_deleteSurroundingText(final int beforeLength, + final int afterLength) { + final Object[] values = { + beforeLength, afterLength + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = { + "RichInputConnectionFinishComposingText" + }; + public static void richInputConnection_finishComposingText() { + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT, + EVENTKEYS_NULLVALUES); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION = { + "RichInputConnectionPerformEditorAction", "imeActionNext" + }; + public static void richInputConnection_performEditorAction(final int imeActionNext) { + final Object[] values = { + imeActionNext + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT = { + "RichInputConnectionSendKeyEvent", "eventTime", "action", "code" + }; + public static void richInputConnection_sendKeyEvent(final KeyEvent keyEvent) { + final Object[] values = { + keyEvent.getEventTime(), + keyEvent.getAction(), + keyEvent.getKeyCode() + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = { + "RichInputConnectionSetComposingText", "text", "newCursorPosition" + }; + public static void richInputConnection_setComposingText(final CharSequence text, + final int newCursorPosition) { + final Object[] values = { + text, newCursorPosition + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION = { + "RichInputConnectionSetSelection", "from", "to" + }; + public static void richInputConnection_setSelection(final int from, final int to) { + final Object[] values = { + from, to + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION, values); + } + private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = { "SuddenJumpingTouchEventHandlerOnTouchEvent", "motionEvent" }; diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java new file mode 100644 index 000000000..4a2cd079c --- /dev/null +++ b/java/src/com/android/inputmethod/research/Statistics.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2012 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.research; + +import com.android.inputmethod.keyboard.Keyboard; + +public class Statistics { + // Number of characters entered during a typing session + int mCharCount; + // Number of letter characters entered during a typing session + int mLetterCount; + // Number of number characters entered + int mNumberCount; + // Number of space characters entered + int mSpaceCount; + // Number of delete operations entered (taps on the backspace key) + int mDeleteKeyCount; + // Number of words entered during a session. + int mWordCount; + // Whether the text field was empty upon editing + boolean mIsEmptyUponStarting; + boolean mIsEmptinessStateKnown; + + // Timers to count average time to enter a key, first press a delete key, + // between delete keys, and then to return typing after a delete key. + final AverageTimeCounter mKeyCounter = new AverageTimeCounter(); + final AverageTimeCounter mBeforeDeleteKeyCounter = new AverageTimeCounter(); + final AverageTimeCounter mDuringRepeatedDeleteKeysCounter = new AverageTimeCounter(); + final AverageTimeCounter mAfterDeleteKeyCounter = new AverageTimeCounter(); + + static class AverageTimeCounter { + int mCount; + int mTotalTime; + + public void reset() { + mCount = 0; + mTotalTime = 0; + } + + public void add(long deltaTime) { + mCount++; + mTotalTime += deltaTime; + } + + public int getAverageTime() { + if (mCount == 0) { + return 0; + } + return mTotalTime / mCount; + } + } + + // To account for the interruptions when the user's attention is directed elsewhere, times + // longer than MIN_TYPING_INTERMISSION are not counted when estimating this statistic. + public static final int MIN_TYPING_INTERMISSION = 5 * 1000; // in milliseconds + public static final int MIN_DELETION_INTERMISSION = 15 * 1000; // in milliseconds + + // The last time that a tap was performed + private long mLastTapTime; + // The type of the last keypress (delete key or not) + boolean mIsLastKeyDeleteKey; + + private static final Statistics sInstance = new Statistics(); + + public static Statistics getInstance() { + return sInstance; + } + + private Statistics() { + reset(); + } + + public void reset() { + mCharCount = 0; + mLetterCount = 0; + mNumberCount = 0; + mSpaceCount = 0; + mDeleteKeyCount = 0; + mWordCount = 0; + mIsEmptyUponStarting = true; + mIsEmptinessStateKnown = false; + mKeyCounter.reset(); + mBeforeDeleteKeyCounter.reset(); + mDuringRepeatedDeleteKeysCounter.reset(); + mAfterDeleteKeyCounter.reset(); + + mLastTapTime = 0; + mIsLastKeyDeleteKey = false; + } + + public void recordChar(int codePoint, long time) { + final long delta = time - mLastTapTime; + if (codePoint == Keyboard.CODE_DELETE) { + mDeleteKeyCount++; + if (delta < MIN_DELETION_INTERMISSION) { + if (mIsLastKeyDeleteKey) { + mDuringRepeatedDeleteKeysCounter.add(delta); + } else { + mBeforeDeleteKeyCounter.add(delta); + } + } + mIsLastKeyDeleteKey = true; + } else { + mCharCount++; + if (Character.isDigit(codePoint)) { + mNumberCount++; + } + if (Character.isLetter(codePoint)) { + mLetterCount++; + } + if (Character.isSpaceChar(codePoint)) { + mSpaceCount++; + } + if (mIsLastKeyDeleteKey && delta < MIN_DELETION_INTERMISSION) { + mAfterDeleteKeyCounter.add(delta); + } else if (!mIsLastKeyDeleteKey && delta < MIN_TYPING_INTERMISSION) { + mKeyCounter.add(delta); + } + mIsLastKeyDeleteKey = false; + } + mLastTapTime = time; + } + + public void recordWordEntered() { + mWordCount++; + } + + public void setIsEmptyUponStarting(final boolean isEmpty) { + mIsEmptyUponStarting = isEmpty; + mIsEmptinessStateKnown = true; + } +} |