aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java16
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java21
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java85
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java (renamed from java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java)115
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java242
-rw-r--r--java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java205
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java326
-rw-r--r--java/src/com/android/inputmethod/latin/Constants.java7
-rw-r--r--java/src/com/android/inputmethod/latin/ImfUtils.java7
-rw-r--r--java/src/com/android/inputmethod/latin/InputAttributes.java2
-rw-r--r--java/src/com/android/inputmethod/latin/InputPointers.java121
-rw-r--r--java/src/com/android/inputmethod/latin/InputView.java13
-rw-r--r--java/src/com/android/inputmethod/latin/LastComposedWord.java2
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java168
-rw-r--r--java/src/com/android/inputmethod/latin/ResizableIntArray.java134
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java1
-rw-r--r--java/src/com/android/inputmethod/latin/Settings.java11
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsValues.java5
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java6
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java2
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java10
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java2
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java (renamed from java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java)76
-rw-r--r--java/src/com/android/inputmethod/research/FeedbackActivity.java52
-rw-r--r--java/src/com/android/inputmethod/research/FeedbackFragment.java73
-rw-r--r--java/src/com/android/inputmethod/research/FeedbackLayout.java62
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLog.java (renamed from java/src/com/android/inputmethod/latin/ResearchLog.java)53
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLogUploader.java223
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLogger.java (renamed from java/src/com/android/inputmethod/latin/ResearchLogger.java)548
32 files changed, 1795 insertions, 813 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index f6376d5f4..4ecbf827a 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -29,7 +29,7 @@ import android.view.ViewConfiguration;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.LatinKeyboardView;
+import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.latin.R;
@@ -37,7 +37,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy();
private InputMethodService mInputMethod;
- private LatinKeyboardView mView;
+ private MainKeyboardView mView;
private AccessibilityEntityProvider mAccessibilityNodeProvider;
private Key mLastHoverKey = null;
@@ -70,7 +70,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
*
* @param view The view to wrap.
*/
- public void setView(LatinKeyboardView view) {
+ public void setView(MainKeyboardView view) {
if (view == null) {
// Ignore null views.
return;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
index 1f3ee7680..b1621a55b 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
@@ -17,7 +17,6 @@
package com.android.inputmethod.keyboard;
import com.android.inputmethod.latin.InputPointers;
-import com.android.inputmethod.latin.SuggestedWords;
public interface KeyboardActionListener {
@@ -73,18 +72,17 @@ public interface KeyboardActionListener {
public void onStartBatchInput();
/**
- * Sends the batch input points data to get updated suggestions
+ * Sends the ongoing batch input points data.
* @param batchPointers the batch input points representing the user input
- * @return updated suggestions that reflects the user input
*/
- public SuggestedWords onUpdateBatchInput(InputPointers batchPointers);
+ public void onUpdateBatchInput(InputPointers batchPointers);
/**
- * Sends a sequence of characters to the listener as batch input.
+ * Sends the final batch input points data.
*
- * @param text the sequence of characters to be displayed as composing text.
+ * @param batchPointers the batch input points representing the user input
*/
- public void onEndBatchInput(CharSequence text);
+ public void onEndBatchInput(InputPointers batchPointers);
/**
* Called when user released a finger outside any key.
@@ -109,9 +107,9 @@ public interface KeyboardActionListener {
@Override
public void onStartBatchInput() {}
@Override
- public SuggestedWords onUpdateBatchInput(InputPointers batchPointers) { return null; }
+ public void onUpdateBatchInput(InputPointers batchPointers) {}
@Override
- public void onEndBatchInput(CharSequence text) {}
+ public void onEndBatchInput(InputPointers batchPointers) {}
@Override
public void onCancelInput() {}
@Override
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 2e4ce199e..d637ab5f1 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -61,7 +61,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
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("Stne.Bold", 7, R.style.KeyboardTheme_Stone_Bold),
+ 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),
};
@@ -71,9 +71,10 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
private boolean mForceNonDistinctMultitouch;
private InputView mCurrentInputView;
- private LatinKeyboardView mKeyboardView;
+ private MainKeyboardView mKeyboardView;
private LatinIME mLatinIME;
private Resources mResources;
+ private SettingsValues mCurrentSettingsValues;
private KeyboardState mState;
@@ -135,6 +136,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
public void loadKeyboard(EditorInfo editorInfo, SettingsValues settingsValues) {
+ mCurrentSettingsValues = settingsValues;
final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
mThemeContext, editorInfo);
builder.setScreenGeometry(mThemeContext.getResources().getConfiguration().orientation,
@@ -170,6 +172,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
private void setKeyboard(final Keyboard keyboard) {
final Keyboard oldKeyboard = mKeyboardView.getKeyboard();
+ mKeyboardView.setGestureInputEnabled(mCurrentSettingsValues.mGestureInputEnabled);
mKeyboardView.setKeyboard(keyboard);
mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding);
mKeyboardView.setKeyPreviewPopupEnabled(
@@ -265,7 +268,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void startDoubleTapTimer() {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getKeyboardView();
if (keyboardView != null) {
final TimerProxy timer = keyboardView.getTimerProxy();
timer.startDoubleTapTimer();
@@ -275,7 +278,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void cancelDoubleTapTimer() {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getKeyboardView();
if (keyboardView != null) {
final TimerProxy timer = keyboardView.getTimerProxy();
timer.cancelDoubleTapTimer();
@@ -285,7 +288,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public boolean isInDoubleTapTimeout() {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getKeyboardView();
return (keyboardView != null)
? keyboardView.getTimerProxy().isInDoubleTapTimeout() : false;
}
@@ -293,7 +296,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void startLongPressTimer(int code) {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getKeyboardView();
if (keyboardView != null) {
final TimerProxy timer = keyboardView.getTimerProxy();
timer.startLongPressTimer(code);
@@ -303,7 +306,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void cancelLongPressTimer() {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getKeyboardView();
if (keyboardView != null) {
final TimerProxy timer = keyboardView.getTimerProxy();
timer.cancelLongPressTimer();
@@ -343,7 +346,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
mState.onCodeInput(code, isSinglePointer(), mLatinIME.getCurrentAutoCapsState());
}
- public LatinKeyboardView getKeyboardView() {
+ public MainKeyboardView getKeyboardView() {
return mKeyboardView;
}
@@ -369,7 +372,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
}
- mKeyboardView = (LatinKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
+ mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
mKeyboardView.setKeyboardActionListener(mLatinIME);
if (mForceNonDistinctMultitouch) {
mKeyboardView.setDistinctMultitouch(false);
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index fb98af3e6..f751fa53c 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -38,10 +38,13 @@ import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
import com.android.inputmethod.latin.StringUtils;
+import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import java.util.HashSet;
@@ -94,7 +97,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
// The maximum key label width in the proportion to the key width.
private static final float MAX_LABEL_RATIO = 0.90f;
- private final static int ALPHA_OPAQUE = 255;
+ private final static int GESTURE_DRAWING_WIDTH = 5;
+ private final static int GESTURE_DRAWING_COLOR = 0xff33b5e5;
// Main keyboard
private Keyboard mKeyboard;
@@ -107,6 +111,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
private int mDelayAfterPreview;
private ViewGroup mPreviewPlacer;
+ /** True if the gesture input is enabled. */
+ protected boolean mGestureInputEnabled;
+
// Drawing
/** True if the entire keyboard needs to be dimmed. */
private boolean mNeedsToDimEntireKeyboard;
@@ -118,11 +125,14 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
private final HashSet<Key> mInvalidatedKeys = new HashSet<Key>();
/** The region of invalidated keys */
private final Rect mInvalidatedKeysRect = new Rect();
+ /** The region of invalidated gestures */
+ private final Rect mInvalidatedGesturesRect = new Rect();
/** The keyboard bitmap buffer for faster updates */
private Bitmap mBuffer;
/** The canvas for the above mutable keyboard bitmap */
private Canvas mCanvas;
private final Paint mPaint = new Paint();
+ private final Paint mGesturePaint = 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>();
@@ -264,7 +274,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
public void blendAlpha(Paint paint) {
final int color = paint.getColor();
- paint.setARGB((paint.getAlpha() * mAnimAlpha) / ALPHA_OPAQUE,
+ paint.setARGB((paint.getAlpha() * mAnimAlpha) / Constants.Color.ALPHA_OPAQUE,
Color.red(color), Color.green(color), Color.blue(color));
}
}
@@ -372,6 +382,13 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout;
mPaint.setAntiAlias(true);
+
+ // TODO: These paint parameters should be specified via attribute of the view and styleable.
+ mGesturePaint.setAntiAlias(true);
+ mGesturePaint.setStyle(Paint.Style.STROKE);
+ mGesturePaint.setStrokeJoin(Paint.Join.ROUND);
+ mGesturePaint.setColor(GESTURE_DRAWING_COLOR);
+ mGesturePaint.setStrokeWidth(GESTURE_DRAWING_WIDTH);
}
// Read fraction value in TypedArray as float.
@@ -426,6 +443,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
return mShowKeyPreviewPopup;
}
+ public void setGestureInputEnabled(boolean gestureInputEnabled) {
+ mGestureInputEnabled = gestureInputEnabled;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mKeyboard != null) {
@@ -499,6 +520,13 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
}
+ // ResearchLogging indicator.
+ // TODO: Reimplement using a keyboard background image specific to the ResearchLogger,
+ // and remove this call.
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().paintIndicator(this, paint, canvas, width, height);
+ }
+
mInvalidatedKeys.clear();
mInvalidatedKeysRect.setEmpty();
mInvalidateAllKeys = false;
@@ -517,7 +545,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
final int keyDrawY = key.mY + getPaddingTop();
canvas.translate(keyDrawX, keyDrawY);
- params.mAnimAlpha = ALPHA_OPAQUE;
+ params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE;
if (!key.isSpacer()) {
onDrawKeyBackground(key, canvas, params);
}
@@ -860,17 +888,60 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker);
}
+ private static class PreviewView extends RelativeLayout {
+ KeyPreviewDrawParams mParams;
+ Paint mGesturePaint;
+
+ public PreviewView(Context context, KeyPreviewDrawParams params, Paint gesturePaint) {
+ super(context);
+ setWillNotDraw(false);
+ mParams = params;
+ mGesturePaint = gesturePaint;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.translate(mParams.mCoordinates[0], mParams.mCoordinates[1]);
+ PointerTracker.drawGestureTrailForAllPointerTrackers(canvas, mGesturePaint);
+ canvas.translate(-mParams.mCoordinates[0], -mParams.mCoordinates[1]);
+ }
+ }
+
private void addKeyPreview(TextView keyPreview) {
if (mPreviewPlacer == null) {
- mPreviewPlacer = new RelativeLayout(getContext());
- final ViewGroup windowContentView =
- (ViewGroup)getRootView().findViewById(android.R.id.content);
- windowContentView.addView(mPreviewPlacer);
+ createPreviewPlacer();
}
mPreviewPlacer.addView(
keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacer, 0, 0));
}
+ private void createPreviewPlacer() {
+ mPreviewPlacer = new PreviewView(getContext(), mKeyPreviewDrawParams, mGesturePaint);
+ final ViewGroup windowContentView =
+ (ViewGroup)getRootView().findViewById(android.R.id.content);
+ windowContentView.addView(mPreviewPlacer);
+ }
+
+ @Override
+ public void showGestureTrail(PointerTracker tracker) {
+ if (mPreviewPlacer == null) {
+ createPreviewPlacer();
+ }
+ final Rect r = tracker.getBoundingBox();
+ if (!r.isEmpty()) {
+ // Invalidate the rectangular region encompassing the gesture. This is needed because
+ // past points along the gesture will fade and gradually disappear.
+ final KeyPreviewDrawParams params = mKeyPreviewDrawParams;
+ mInvalidatedGesturesRect.set(r);
+ mInvalidatedGesturesRect.offset(params.mCoordinates[0], params.mCoordinates[1]);
+ mInvalidatedGesturesRect.inset(-GESTURE_DRAWING_WIDTH, -GESTURE_DRAWING_WIDTH);
+ mPreviewPlacer.invalidate(mInvalidatedGesturesRect);
+ } else {
+ mPreviewPlacer.invalidate();
+ }
+ }
+
@SuppressWarnings("deprecation") // setBackgroundDrawable is replaced by setBackground in API16
@Override
public void showKeyPreview(PointerTracker tracker) {
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 7714ba892..8c234e4e6 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -43,16 +43,17 @@ import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.ResearchLogger;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
import com.android.inputmethod.latin.StringUtils;
import com.android.inputmethod.latin.SubtypeLocale;
import com.android.inputmethod.latin.Utils;
import com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import java.util.Locale;
import java.util.WeakHashMap;
@@ -64,9 +65,9 @@ import java.util.WeakHashMap;
* @attr ref R.styleable#KeyboardView_verticalCorrection
* @attr ref R.styleable#KeyboardView_popupLayout
*/
-public class LatinKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
+public class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
SuddenJumpingTouchEventHandler.ProcessMotionEvent {
- private static final String TAG = LatinKeyboardView.class.getSimpleName();
+ private static final String TAG = MainKeyboardView.class.getSimpleName();
// TODO: Kill process when the usability study mode was changed.
private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
@@ -80,10 +81,9 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
// Stuff to draw language name on spacebar.
private final int mLanguageOnSpacebarFinalAlpha;
private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
- private static final int ALPHA_OPAQUE = 255;
private boolean mNeedsToDisplayLanguage;
private boolean mHasMultipleEnabledIMEsOrSubtypes;
- private int mLanguageOnSpacebarAnimAlpha = ALPHA_OPAQUE;
+ private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
private final float mSpacebarTextRatio;
private float mSpacebarTextSize;
private final int mSpacebarTextColor;
@@ -99,7 +99,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
// Stuff to draw altCodeWhileTyping keys.
private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
- private int mAltCodeKeyWhileTypingAnimAlpha = ALPHA_OPAQUE;
+ private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
// More keys keyboard
private PopupWindow mMoreKeysWindow;
@@ -119,7 +119,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
private final KeyTimerHandler mKeyTimerHandler;
- private static class KeyTimerHandler extends StaticInnerHandlerWrapper<LatinKeyboardView>
+ private static class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView>
implements TimerProxy {
private static final int MSG_REPEAT_KEY = 1;
private static final int MSG_LONGPRESS_KEY = 2;
@@ -128,14 +128,14 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
private final KeyTimerParams mParams;
- public KeyTimerHandler(LatinKeyboardView outerInstance, KeyTimerParams params) {
+ public KeyTimerHandler(MainKeyboardView outerInstance, KeyTimerParams params) {
super(outerInstance);
mParams = params;
}
@Override
public void handleMessage(Message msg) {
- final LatinKeyboardView keyboardView = getOuterInstance();
+ final MainKeyboardView keyboardView = getOuterInstance();
final PointerTracker tracker = (PointerTracker) msg.obj;
switch (msg.what) {
case MSG_REPEAT_KEY:
@@ -249,7 +249,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
if (isTyping) {
return;
}
- final LatinKeyboardView keyboardView = getOuterInstance();
+ final MainKeyboardView keyboardView = getOuterInstance();
cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
}
@@ -299,13 +299,13 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
mTouchNoiseThresholdDistance = 0;
}
- public PointerTrackerParams(TypedArray latinKeyboardViewAttr) {
- mSlidingKeyInputEnabled = latinKeyboardViewAttr.getBoolean(
- R.styleable.LatinKeyboardView_slidingKeyInputEnable, false);
- mTouchNoiseThresholdTime = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_touchNoiseThresholdTime, 0);
- mTouchNoiseThresholdDistance = latinKeyboardViewAttr.getDimension(
- R.styleable.LatinKeyboardView_touchNoiseThresholdDistance, 0);
+ public PointerTrackerParams(TypedArray mainKeyboardViewAttr) {
+ mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean(
+ R.styleable.MainKeyboardView_slidingKeyInputEnable, false);
+ mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
+ mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimension(
+ R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0);
}
}
@@ -316,65 +316,67 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
public final int mLongPressShiftKeyTimeout;
public final int mIgnoreAltCodeKeyTimeout;
- public KeyTimerParams(TypedArray latinKeyboardViewAttr) {
- mKeyRepeatStartTimeout = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_keyRepeatStartTimeout, 0);
- mKeyRepeatInterval = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_keyRepeatInterval, 0);
- mLongPressKeyTimeout = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_longPressKeyTimeout, 0);
- mLongPressShiftKeyTimeout = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_longPressShiftKeyTimeout, 0);
- mIgnoreAltCodeKeyTimeout = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_ignoreAltCodeKeyTimeout, 0);
+ public KeyTimerParams(TypedArray mainKeyboardViewAttr) {
+ mKeyRepeatStartTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0);
+ mKeyRepeatInterval = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_keyRepeatInterval, 0);
+ mLongPressKeyTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_longPressKeyTimeout, 0);
+ mLongPressShiftKeyTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_longPressShiftKeyTimeout, 0);
+ mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
}
}
- public LatinKeyboardView(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.latinKeyboardViewStyle);
+ public MainKeyboardView(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.mainKeyboardViewStyle);
}
- public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
+ public MainKeyboardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this);
mHasDistinctMultitouch = context.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
+ final Resources res = getResources();
final boolean needsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
- Utils.getDeviceOverrideValue(context.getResources(),
+ Utils.getDeviceOverrideValue(res,
R.array.phantom_sudden_move_event_device_list, "false"));
PointerTracker.init(mHasDistinctMultitouch, needsPhantomSuddenMoveEventHack);
final TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.LatinKeyboardView, defStyle, R.style.LatinKeyboardView);
+ attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
mAutoCorrectionSpacebarLedEnabled = a.getBoolean(
- R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedEnabled, false);
+ R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false);
mAutoCorrectionSpacebarLedIcon = a.getDrawable(
- R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedIcon);
- mSpacebarTextRatio = a.getFraction(R.styleable.LatinKeyboardView_spacebarTextRatio,
+ R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon);
+ mSpacebarTextRatio = a.getFraction(R.styleable.MainKeyboardView_spacebarTextRatio,
1000, 1000, 1) / 1000.0f;
- mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboardView_spacebarTextColor, 0);
+ mSpacebarTextColor = a.getColor(R.styleable.MainKeyboardView_spacebarTextColor, 0);
mSpacebarTextShadowColor = a.getColor(
- R.styleable.LatinKeyboardView_spacebarTextShadowColor, 0);
+ R.styleable.MainKeyboardView_spacebarTextShadowColor, 0);
mLanguageOnSpacebarFinalAlpha = a.getInt(
- R.styleable.LatinKeyboardView_languageOnSpacebarFinalAlpha, ALPHA_OPAQUE);
+ R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha,
+ Constants.Color.ALPHA_OPAQUE);
final int languageOnSpacebarFadeoutAnimatorResId = a.getResourceId(
- R.styleable.LatinKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
+ R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
final int altCodeKeyWhileTypingFadeoutAnimatorResId = a.getResourceId(
- R.styleable.LatinKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
+ R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
final int altCodeKeyWhileTypingFadeinAnimatorResId = a.getResourceId(
- R.styleable.LatinKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
+ R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
final KeyTimerParams keyTimerParams = new KeyTimerParams(a);
mPointerTrackerParams = new PointerTrackerParams(a);
final float keyHysteresisDistance = a.getDimension(
- R.styleable.LatinKeyboardView_keyHysteresisDistance, 0);
+ R.styleable.MainKeyboardView_keyHysteresisDistance, 0);
mKeyDetector = new KeyDetector(keyHysteresisDistance);
mKeyTimerHandler = new KeyTimerHandler(this, keyTimerParams);
mConfigShowMoreKeysKeyboardAtTouchedPoint = a.getBoolean(
- R.styleable.LatinKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
+ R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
a.recycle();
PointerTracker.setParameters(mPointerTrackerParams);
@@ -459,17 +461,17 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
super.setKeyboard(keyboard);
mKeyDetector.setKeyboard(
keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
- PointerTracker.setKeyDetector(mKeyDetector);
+ PointerTracker.setKeyDetector(mKeyDetector, mGestureInputEnabled);
mTouchScreenRegulator.setKeyboard(keyboard);
mMoreKeysPanelCache.clear();
mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE);
mSpaceIcon = (mSpaceKey != null)
- ? mSpaceKey.getIcon(keyboard.mIconsSet, ALPHA_OPAQUE) : null;
+ ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinKeyboardView_setKeyboard(keyboard);
+ ResearchLogger.mainKeyboardView_setKeyboard(keyboard);
}
// This always needs to be set since the accessibility state can
@@ -507,6 +509,17 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ // Notify the research logger that the keyboard view has been attached. This is needed
+ // 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();
+ }
+ }
+
+ @Override
public void cancelAllMessages() {
mKeyTimerHandler.cancelAllMessages();
super.cancelAllMessages();
@@ -555,7 +568,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
*/
protected boolean onLongPress(Key parentKey, PointerTracker tracker) {
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinKeyboardView_onLongPress();
+ ResearchLogger.mainKeyboardView_onLongPress();
}
final int primaryCode = parentKey.mCode;
if (parentKey.hasEmbeddedMoreKey()) {
@@ -706,7 +719,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
}
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinKeyboardView_processMotionEvent(me, action, eventTime, index, id,
+ ResearchLogger.mainKeyboardView_processMotionEvent(me, action, eventTime, index, id,
x, y);
}
@@ -778,7 +791,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
+ pointerSize + "," + pointerPressure);
}
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinKeyboardView_processMotionEvent(me, action, eventTime,
+ ResearchLogger.mainKeyboardView_processMotionEvent(me, action, eventTime,
i, pointerId, px, py);
}
}
@@ -867,7 +880,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
mNeedsToDisplayLanguage = false;
} else {
if (subtypeChanged && needsToDisplayLanguage) {
- setLanguageOnSpacebarAnimAlpha(ALPHA_OPAQUE);
+ setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
if (animator.isStarted()) {
animator.cancel();
}
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index 9c8069194..870eff29f 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -25,6 +25,7 @@ import android.widget.PopupWindow;
import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
+import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.R;
/**
@@ -63,8 +64,13 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
}
@Override
- public void onEndBatchInput(CharSequence text) {
- mListener.onEndBatchInput(text);
+ public void onUpdateBatchInput(InputPointers batchPointers) {
+ mListener.onUpdateBatchInput(batchPointers);
+ }
+
+ @Override
+ public void onEndBatchInput(InputPointers batchPointers) {
+ mListener.onEndBatchInput(batchPointers);
}
@Override
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 733d3b09b..4a5ecf986 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -16,17 +16,22 @@
package com.android.inputmethod.keyboard;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
-import com.android.inputmethod.keyboard.internal.GestureTracker;
+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.InputPointers;
import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.ResearchLogger;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import java.util.ArrayList;
@@ -37,6 +42,11 @@ public class PointerTracker {
private static final boolean DEBUG_LISTENER = false;
private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
+ // TODO: There should be an option to turn on/off the gesture input.
+ private static boolean sIsGestureEnabled = true;
+
+ private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec
+
public interface KeyEventHandler {
/**
* Get KeyDetector object that is used for this PointerTracker.
@@ -69,6 +79,7 @@ public class PointerTracker {
public TextView inflateKeyPreviewText();
public void showKeyPreview(PointerTracker tracker);
public void dismissKeyPreview(PointerTracker tracker);
+ public void showGestureTrail(PointerTracker tracker);
}
public interface TimerProxy {
@@ -108,12 +119,18 @@ public class PointerTracker {
}
// Parameters for pointer handling.
- private static LatinKeyboardView.PointerTrackerParams sParams;
+ private static MainKeyboardView.PointerTrackerParams sParams;
private static int sTouchNoiseThresholdDistanceSquared;
private static boolean sNeedsPhantomSuddenMoveEventHack;
private static final ArrayList<PointerTracker> sTrackers = new ArrayList<PointerTracker>();
+ private static final InputPointers sAggregratedPointers = new InputPointers(
+ GestureStroke.DEFAULT_CAPACITY);
private static PointerTrackerQueue sPointerTrackerQueue;
+ // HACK: Change gesture detection criteria depending on this variable.
+ // TODO: Find more comprehensive ways to detect a gesture start.
+ // True when the previous user input was a gesture input, not a typing input.
+ private static boolean sWasInGesture;
public final int mPointerId;
@@ -126,6 +143,14 @@ public class PointerTracker {
private int mKeyQuarterWidthSquared;
private final TextView mKeyPreviewText;
+ private boolean mIsAlphabetKeyboard;
+ private boolean mIsPossibleGesture = false;
+ private boolean mInGesture = false;
+
+ // TODO: Remove these variables
+ private int mLastRecognitionPointSize = 0;
+ private long mLastRecognitionTime = 0;
+
// The position and time at which first down event occurred.
private long mDownTime;
private long mUpTime;
@@ -162,8 +187,7 @@ public class PointerTracker {
private static final KeyboardActionListener EMPTY_LISTENER =
new KeyboardActionListener.Adapter();
- // Gesture tracker singleton instance
- private static final GestureTracker sGestureTracker = GestureTracker.getInstance();
+ private final GestureStroke mGestureStroke;
public static void init(boolean hasDistinctMultitouch,
boolean needsPhantomSuddenMoveEventHack) {
@@ -174,15 +198,27 @@ public class PointerTracker {
}
sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
- setParameters(LatinKeyboardView.PointerTrackerParams.DEFAULT);
+ setParameters(MainKeyboardView.PointerTrackerParams.DEFAULT);
+ updateGestureInputEnabledState(null, false /* gestureInputEnabled */);
}
- public static void setParameters(LatinKeyboardView.PointerTrackerParams params) {
+ public static void setParameters(MainKeyboardView.PointerTrackerParams params) {
sParams = params;
sTouchNoiseThresholdDistanceSquared = (int)(
params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance);
}
+ private static void updateGestureInputEnabledState(Keyboard keyboard,
+ boolean gestureInputEnabled) {
+ if (!gestureInputEnabled
+ || AccessibilityUtils.getInstance().isTouchExplorationEnabled()
+ || (keyboard != null && keyboard.mId.passwordInput())) {
+ sIsGestureEnabled = false;
+ } else {
+ sIsGestureEnabled = true;
+ }
+ }
+
public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) {
final ArrayList<PointerTracker> trackers = sTrackers;
@@ -200,32 +236,83 @@ public class PointerTracker {
}
public static void setKeyboardActionListener(KeyboardActionListener listener) {
- for (final PointerTracker tracker : sTrackers) {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
tracker.mListener = listener;
}
- GestureTracker.init(listener);
}
- public static void setKeyDetector(KeyDetector keyDetector) {
- for (final PointerTracker tracker : sTrackers) {
+ public static void setKeyDetector(KeyDetector keyDetector, boolean gestureInputEnabledByUser) {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
tracker.setKeyDetectorInner(keyDetector);
// Mark that keyboard layout has been changed.
tracker.mKeyboardLayoutHasBeenChanged = true;
}
- sGestureTracker.setKeyboard(keyDetector.getKeyboard());
+ final Keyboard keyboard = keyDetector.getKeyboard();
+ updateGestureInputEnabledState(keyboard, gestureInputEnabledByUser);
}
public static void dismissAllKeyPreviews() {
- for (final PointerTracker tracker : sTrackers) {
+ 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);
}
}
- public PointerTracker(int id, KeyEventHandler handler) {
+ // TODO: To handle multi-touch gestures we may want to move this method to
+ // {@link PointerTrackerQueue}.
+ private static InputPointers getIncrementalBatchPoints() {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
+ tracker.mGestureStroke.appendIncrementalBatchPoints(sAggregratedPointers);
+ }
+ return sAggregratedPointers;
+ }
+
+ // TODO: To handle multi-touch gestures we may want to move this method to
+ // {@link PointerTrackerQueue}.
+ private static InputPointers getAllBatchPoints() {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
+ tracker.mGestureStroke.appendAllBatchPoints(sAggregratedPointers);
+ }
+ return sAggregratedPointers;
+ }
+
+ // TODO: To handle multi-touch gestures we may want to move this method to
+ // {@link PointerTrackerQueue}.
+ public static void clearBatchInputPointsOfAllPointerTrackers() {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
+ tracker.mGestureStroke.reset();
+ }
+ sAggregratedPointers.reset();
+ }
+
+ // TODO: To handle multi-touch gestures we may want to move this method to
+ // {@link PointerTrackerQueue}.
+ public static void drawGestureTrailForAllPointerTrackers(Canvas canvas, Paint paint) {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
+ tracker.mGestureStroke.drawGestureTrail(canvas, paint, tracker.getLastX(),
+ tracker.getLastY());
+ }
+ }
+
+ private PointerTracker(int id, KeyEventHandler handler) {
if (handler == null)
throw new NullPointerException();
mPointerId = id;
+ mGestureStroke = new GestureStroke(id);
setKeyDetectorInner(handler.getKeyDetector());
mListener = handler.getKeyboardActionListener();
mDrawingProxy = handler.getDrawingProxy();
@@ -239,7 +326,7 @@ public class PointerTracker {
// Returns true if keyboard has been changed by this callback.
private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) {
- if (sGestureTracker.isInGesture()) {
+ if (mInGesture) {
return false;
}
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
@@ -295,7 +382,7 @@ public class PointerTracker {
// Note that we need primaryCode argument because the keyboard may in shifted state and the
// primaryCode is different from {@link Key#mCode}.
private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) {
- if (sGestureTracker.isInGesture()) {
+ if (mInGesture) {
return;
}
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
@@ -328,6 +415,9 @@ public class PointerTracker {
private void setKeyDetectorInner(KeyDetector keyDetector) {
mKeyDetector = keyDetector;
mKeyboard = keyDetector.getKeyboard();
+ mIsAlphabetKeyboard = mKeyboard.mId.isAlphabetKeyboard();
+ mGestureStroke.setGestureSampleLength(
+ mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
if (newKey != mCurrentKey) {
if (mDrawingProxy != null) {
@@ -398,7 +488,7 @@ public class PointerTracker {
return;
}
- if (!key.noKeyPreview() && !sGestureTracker.isInGesture()) {
+ if (!key.noKeyPreview() && !mInGesture) {
mDrawingProxy.showKeyPreview(this);
}
updatePressKeyGraphics(key);
@@ -446,6 +536,9 @@ public class PointerTracker {
public long getDownTime() {
return mDownTime;
}
+ public Rect getBoundingBox() {
+ return mGestureStroke.getBoundingBox();
+ }
private Key onDownKey(int x, int y, long eventTime) {
mDownTime = eventTime;
@@ -469,6 +562,53 @@ public class PointerTracker {
return newKey;
}
+ private void startBatchInput() {
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, "onStartBatchInput");
+ }
+ mInGesture = true;
+ mListener.onStartBatchInput();
+ }
+
+ private void updateBatchInput(InputPointers batchPoints) {
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
+ }
+ mListener.onUpdateBatchInput(batchPoints);
+ }
+
+ private void endBatchInput(InputPointers batchPoints) {
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, "onEndBatchInput: batchPoints=" + batchPoints.getPointerSize());
+ }
+ mListener.onEndBatchInput(batchPoints);
+ clearBatchInputRecognitionStateOfThisPointerTracker();
+ clearBatchInputPointsOfAllPointerTrackers();
+ sWasInGesture = true;
+ }
+
+ private void abortBatchInput() {
+ clearBatchInputRecognitionStateOfThisPointerTracker();
+ clearBatchInputPointsOfAllPointerTrackers();
+ }
+
+ private void clearBatchInputRecognitionStateOfThisPointerTracker() {
+ mIsPossibleGesture = false;
+ mInGesture = false;
+ mLastRecognitionPointSize = 0;
+ mLastRecognitionTime = 0;
+ }
+
+ private boolean updateBatchInputRecognitionState(long eventTime, int size) {
+ if (size > mLastRecognitionPointSize
+ && eventTime > mLastRecognitionTime + MIN_GESTURE_RECOGNITION_TIME) {
+ mLastRecognitionPointSize = size;
+ mLastRecognitionTime = eventTime;
+ return true;
+ }
+ return false;
+ }
+
public void processMotionEvent(int action, int x, int y, long eventTime,
KeyEventHandler handler) {
switch (action) {
@@ -527,7 +667,15 @@ public class PointerTracker {
}
onDownEventInternal(x, y, eventTime);
if (queue != null && queue.size() == 1) {
- sGestureTracker.onDownEvent(this, x, y, eventTime, key);
+ mIsPossibleGesture = false;
+ // A gesture should start only from the letter key.
+ if (sIsGestureEnabled && mIsAlphabetKeyboard && !mIsShowingMoreKeysPanel && key != null
+ && Keyboard.isLetterCode(key.mCode)) {
+ mIsPossibleGesture = true;
+ // TODO: pointer times should be relative to first down even in entire batch input
+ // instead of resetting to 0 for each new down event.
+ mGestureStroke.addPoint(x, y, 0, false);
+ }
}
}
@@ -563,6 +711,26 @@ public class PointerTracker {
mIsInSlidingKeyInput = true;
}
+ private void onGestureMoveEvent(PointerTracker tracker, int x, int y, long eventTime,
+ boolean isHistorical, Key key) {
+ final int gestureTime = (int)(eventTime - tracker.getDownTime());
+ if (sIsGestureEnabled && mIsPossibleGesture) {
+ final GestureStroke stroke = mGestureStroke;
+ stroke.addPoint(x, y, gestureTime, isHistorical);
+ if (!mInGesture && stroke.isStartOfAGesture(gestureTime, sWasInGesture)) {
+ startBatchInput();
+ }
+ }
+
+ if (key != null && mInGesture) {
+ final InputPointers batchPoints = getIncrementalBatchPoints();
+ mDrawingProxy.showGestureTrail(this);
+ if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
+ updateBatchInput(batchPoints);
+ }
+ }
+ }
+
public void onMoveEvent(int x, int y, long eventTime, MotionEvent me) {
if (DEBUG_MOVE_EVENT)
printTouchEvent("onMoveEvent:", x, y, eventTime);
@@ -577,7 +745,7 @@ public class PointerTracker {
final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
final long historicalTime = me.getHistoricalEventTime(h);
- sGestureTracker.onMoveEvent(this, historicalX, historicalY, historicalTime,
+ onGestureMoveEvent(this, historicalX, historicalY, historicalTime,
true /* isHistorical */, null);
}
}
@@ -588,8 +756,8 @@ public class PointerTracker {
Key key = onMoveKey(x, y);
// Register move event on gesture tracker.
- sGestureTracker.onMoveEvent(this, x, y, eventTime, false, key);
- if (sGestureTracker.isInGesture()) {
+ onGestureMoveEvent(this, x, y, eventTime, false /* isHistorical */, key);
+ if (mInGesture) {
mIgnoreModifierKey = true;
mTimerProxy.cancelLongPressTimer();
mIsInSlidingKeyInput = true;
@@ -686,7 +854,7 @@ public class PointerTracker {
final PointerTrackerQueue queue = sPointerTrackerQueue;
if (queue != null) {
- if (!sGestureTracker.isInGesture()) {
+ if (!mInGesture) {
if (mCurrentKey != null && mCurrentKey.isModifier()) {
// Before processing an up event of modifier key, all pointers already being
// tracked should be released.
@@ -720,23 +888,21 @@ public class PointerTracker {
mIsShowingMoreKeysPanel = false;
}
- if (sGestureTracker.isInGesture()) {
+ if (mInGesture) {
// Register up event on gesture tracker.
- sGestureTracker.onUpEvent(this, x, y, eventTime);
- if (!sPointerTrackerQueue.isAnyInSlidingKeyInput()) {
- // TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code.
- sGestureTracker.endBatchInput();
- }
+ // TODO: Figure out how to deal with multiple fingers that are in gesture, sliding,
+ // and/or tapping mode?
+ endBatchInput(getAllBatchPoints());
if (mCurrentKey != null) {
callListenerOnRelease(mCurrentKey, mCurrentKey.mCode, true);
+ mCurrentKey = null;
}
- mCurrentKey = null;
+ mDrawingProxy.showGestureTrail(this);
return;
- } else {
- // TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code.
- sGestureTracker.endBatchInput();
}
-
+ // This event will be recognized as a regular code input. Clear unused batch points so they
+ // are not mistakenly included in the next batch event.
+ clearBatchInputPointsOfAllPointerTrackers();
if (mKeyAlreadyProcessed)
return;
if (mCurrentKey != null && !mCurrentKey.isRepeatable()) {
@@ -745,11 +911,10 @@ public class PointerTracker {
}
public void onShowMoreKeysPanel(int x, int y, KeyEventHandler handler) {
+ abortBatchInput();
onLongPressed();
- onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
mIsShowingMoreKeysPanel = true;
- // TODO: Calls to beginBatchInput() is missing in this class. Reorganize the code.
- sGestureTracker.abortBatchInput();
+ onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
}
public void onLongPressed() {
@@ -784,7 +949,7 @@ public class PointerTracker {
}
private void startRepeatKey(Key key) {
- if (key != null && key.isRepeatable() && !sGestureTracker.isInGesture()) {
+ if (key != null && key.isRepeatable() && !mInGesture) {
onRegisterKey(key);
mTimerProxy.startKeyRepeatTimer(this);
}
@@ -814,7 +979,7 @@ public class PointerTracker {
}
private void startLongPressTimer(Key key) {
- if (key != null && key.isLongPressEnabled() && !sGestureTracker.isInGesture()) {
+ if (key != null && key.isLongPressEnabled() && !mInGesture) {
mTimerProxy.startLongPressTimer(this);
}
}
@@ -828,6 +993,7 @@ public class PointerTracker {
int code = key.mCode;
callListenerOnCodeInput(key, code, x, y);
callListenerOnRelease(key, code, false);
+ sWasInGesture = false;
}
private void printTouchEvent(String title, int x, int y, long eventTime) {
diff --git a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java b/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
index 107138395..2398c0850 100644
--- a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
@@ -22,9 +22,9 @@ import android.view.MotionEvent;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.ResearchLogger;
import com.android.inputmethod.latin.Utils;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
public class SuddenJumpingTouchEventHandler {
private static final String TAG = SuddenJumpingTouchEventHandler.class.getSimpleName();
@@ -70,7 +70,7 @@ public class SuddenJumpingTouchEventHandler {
* the sudden moves subside, a DOWN event is simulated for the second key.
* @param me the motion event
* @return true if the event was consumed, so that it doesn't continue to be handled by
- * {@link LatinKeyboardView}.
+ * {@link MainKeyboardView}.
*/
private boolean handleSuddenJumping(MotionEvent me) {
if (!mNeedsSuddenJumpingHack)
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
new file mode 100644
index 000000000..c16b70ef0
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
@@ -0,0 +1,205 @@
+/*
+ * 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.keyboard.internal;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.FloatMath;
+
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.InputPointers;
+import com.android.inputmethod.latin.ResizableIntArray;
+
+public class GestureStroke {
+ public static final int DEFAULT_CAPACITY = 128;
+
+ private final int mPointerId;
+ private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY);
+ private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
+ private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
+ private float mLength;
+ private float mAngle;
+ private int mIncrementalRecognitionSize;
+ private int mLastIncrementalBatchSize;
+ private long mLastPointTime;
+ private int mLastPointX;
+ private int mLastPointY;
+
+ private int mMinGestureLength;
+ private int mMinGestureLengthWhileInGesture;
+ private int mMinGestureSampleLength;
+ private final Rect mDrawingRect = new Rect();
+
+ // TODO: Move some of these to resource.
+ private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH = 1.0f;
+ private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH_WHILE_IN_GESTURE = 0.5f;
+ private static final int MIN_GESTURE_DURATION = 150; // msec
+ private static final int MIN_GESTURE_DURATION_WHILE_IN_GESTURE = 75; // msec
+ private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT = 1.0f / 6.0f;
+ private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f; // dip/msec
+ private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float)(Math.PI / 4.0f);
+
+ private static final float DOUBLE_PI = (float)(2 * Math.PI);
+
+ // Fade based on number of gesture samples, see MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT
+ private static final int DRAWING_GESTURE_FADE_START = 10;
+ private static final int DRAWING_GESTURE_FADE_RATE = 6;
+
+ public GestureStroke(int pointerId) {
+ mPointerId = pointerId;
+ reset();
+ }
+
+ public void setGestureSampleLength(final int keyWidth, final int keyHeight) {
+ // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
+ mMinGestureLength = (int)(keyWidth * MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH);
+ mMinGestureLengthWhileInGesture = (int)(
+ keyWidth * MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH_WHILE_IN_GESTURE);
+ mMinGestureSampleLength = (int)(keyHeight * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT);
+ }
+
+ public boolean isStartOfAGesture(final int downDuration, final boolean wasInGesture) {
+ // The tolerance of the time duration and the stroke length to detect the start of a
+ // gesture stroke should be eased when the previous input was a gesture input.
+ if (wasInGesture) {
+ return downDuration > MIN_GESTURE_DURATION_WHILE_IN_GESTURE
+ && mLength > mMinGestureLengthWhileInGesture;
+ }
+ return downDuration > MIN_GESTURE_DURATION && mLength > mMinGestureLength;
+ }
+
+ public void reset() {
+ mLength = 0;
+ mAngle = 0;
+ mIncrementalRecognitionSize = 0;
+ mLastIncrementalBatchSize = 0;
+ mLastPointTime = 0;
+ mEventTimes.setLength(0);
+ mXCoordinates.setLength(0);
+ mYCoordinates.setLength(0);
+ mDrawingRect.setEmpty();
+ }
+
+ private void updateLastPoint(final int x, final int y, final int time) {
+ mLastPointTime = time;
+ mLastPointX = x;
+ mLastPointY = y;
+ }
+
+ public void addPoint(final int x, final int y, final int time, final boolean isHistorical) {
+ final int size = mEventTimes.getLength();
+ if (size == 0) {
+ mEventTimes.add(time);
+ mXCoordinates.add(x);
+ mYCoordinates.add(y);
+ if (!isHistorical) {
+ updateLastPoint(x, y, time);
+ }
+ return;
+ }
+
+ final int lastX = mXCoordinates.get(size - 1);
+ final int lastY = mYCoordinates.get(size - 1);
+ final float dist = getDistance(lastX, lastY, x, y);
+ if (dist > mMinGestureSampleLength) {
+ mEventTimes.add(time);
+ mXCoordinates.add(x);
+ mYCoordinates.add(y);
+ mLength += dist;
+ final float angle = getAngle(lastX, lastY, x, y);
+ if (size > 1) {
+ final float curvature = getAngleDiff(angle, mAngle);
+ if (curvature > GESTURE_RECOG_CURVATURE_THRESHOLD) {
+ if (size > mIncrementalRecognitionSize) {
+ mIncrementalRecognitionSize = size;
+ }
+ }
+ }
+ mAngle = angle;
+ }
+
+ if (!isHistorical) {
+ final int duration = (int)(time - mLastPointTime);
+ if (mLastPointTime != 0 && duration > 0) {
+ final float speed = getDistance(mLastPointX, mLastPointY, x, y) / duration;
+ if (speed < GESTURE_RECOG_SPEED_THRESHOLD) {
+ mIncrementalRecognitionSize = size;
+ }
+ }
+ updateLastPoint(x, y, time);
+ }
+ }
+
+ public void appendAllBatchPoints(final InputPointers out) {
+ appendBatchPoints(out, mEventTimes.getLength());
+ }
+
+ public void appendIncrementalBatchPoints(final InputPointers out) {
+ appendBatchPoints(out, mIncrementalRecognitionSize);
+ }
+
+ private void appendBatchPoints(final InputPointers out, final int size) {
+ out.append(mPointerId, mEventTimes, mXCoordinates, mYCoordinates,
+ mLastIncrementalBatchSize, size - mLastIncrementalBatchSize);
+ mLastIncrementalBatchSize = size;
+ }
+
+ private static float getDistance(final int p1x, final int p1y,
+ final int p2x, final int p2y) {
+ final float dx = p1x - p2x;
+ final float dy = p1y - p2y;
+ // TODO: Optimize out this {@link FloatMath#sqrt(float)} call.
+ return FloatMath.sqrt(dx * dx + dy * dy);
+ }
+
+ private static float getAngle(final int p1x, final int p1y, final int p2x, final int p2y) {
+ final int dx = p1x - p2x;
+ final int dy = p1y - p2y;
+ if (dx == 0 && dy == 0) return 0;
+ return (float)Math.atan2(dy, dx);
+ }
+
+ private static float getAngleDiff(final float a1, final float a2) {
+ final float diff = Math.abs(a1 - a2);
+ if (diff > Math.PI) {
+ return DOUBLE_PI - diff;
+ }
+ return diff;
+ }
+
+ public void drawGestureTrail(Canvas canvas, Paint paint, int lastX, int lastY) {
+ // TODO: These paint parameter interpolation should be tunable, possibly introduce an object
+ // that implements an interface such as Paint getPaint(int step, int strokePoints)
+ final int size = mXCoordinates.getLength();
+ int[] xCoords = mXCoordinates.getPrimitiveArray();
+ int[] yCoords = mYCoordinates.getPrimitiveArray();
+ int alpha = Constants.Color.ALPHA_OPAQUE;
+ for (int i = size - 1; i > 0 && alpha > 0; i--) {
+ paint.setAlpha(alpha);
+ if (size - i > DRAWING_GESTURE_FADE_START) {
+ alpha -= DRAWING_GESTURE_FADE_RATE;
+ }
+ canvas.drawLine(xCoords[i - 1], yCoords[i - 1], xCoords[i], yCoords[i], paint);
+ if (i == size - 1) {
+ canvas.drawLine(lastX, lastY, xCoords[i], yCoords[i], paint);
+ }
+ }
+ }
+
+ public Rect getBoundingBox() {
+ return mDrawingRect;
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java
deleted file mode 100644
index dfd697a7a..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureTracker.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * 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.keyboard.internal;
-
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardActionListener;
-import com.android.inputmethod.keyboard.PointerTracker;
-import com.android.inputmethod.latin.InputPointers;
-import com.android.inputmethod.latin.SuggestedWords;
-
-// TODO: Remove this class by consolidating with PointerTracker
-public class GestureTracker {
- private static final String TAG = GestureTracker.class.getSimpleName();
- private static final boolean DEBUG_LISTENER = false;
-
- // TODO: There should be an option to turn on/off the gesture input.
- private static final boolean GESTURE_ON = true;
-
- private static final GestureTracker sInstance = new GestureTracker();
-
- private static final int MIN_RECOGNITION_TIME = 100;
- private static final int MIN_GESTURE_DURATION = 200;
-
- private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f;
- private static final float SQUARED_GESTURE_RECOG_SPEED_THRESHOLD =
- GESTURE_RECOG_SPEED_THRESHOLD * GESTURE_RECOG_SPEED_THRESHOLD;
- private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float) (Math.PI / 4);
-
- private boolean mIsAlphabetKeyboard;
- private boolean mIsPossibleGesture = false;
- private boolean mInGesture = false;
-
- private KeyboardActionListener mListener;
- private SuggestedWords mSuggestions;
-
- private final SparseArray<GestureStroke> mGestureStrokes = new SparseArray<GestureStroke>();
-
- private int mLastRecognitionPointSize = 0;
- private long mLastRecognitionTime = 0;
-
- public static void init(KeyboardActionListener listner) {
- sInstance.mListener = listner;
- }
-
- public static GestureTracker getInstance() {
- return sInstance;
- }
-
- private GestureTracker() {
- }
-
- public void setKeyboard(Keyboard keyboard) {
- mIsAlphabetKeyboard = keyboard.mId.isAlphabetKeyboard();
- GestureStroke.setGestureSampleLength(keyboard.mMostCommonKeyWidth / 2,
- keyboard.mMostCommonKeyHeight / 6);
- }
-
- private void startBatchInput() {
- if (DEBUG_LISTENER) {
- Log.d(TAG, "onStartBatchInput");
- }
- mInGesture = true;
- mListener.onStartBatchInput();
- mSuggestions = null;
- }
-
- // TODO: The corresponding startBatchInput() is a private method. Reorganize the code.
- public void endBatchInput() {
- if (isInGesture() && mSuggestions != null && mSuggestions.size() > 0) {
- final CharSequence text = mSuggestions.getWord(0);
- if (DEBUG_LISTENER) {
- Log.d(TAG, "onEndBatchInput: text=" + text);
- }
- mListener.onEndBatchInput(text);
- }
- mInGesture = false;
- clearBatchInputPoints();
- }
-
- public void abortBatchInput() {
- mIsPossibleGesture = false;
- mInGesture = false;
- }
-
- public boolean isInGesture() {
- return mInGesture;
- }
-
- public void onDownEvent(PointerTracker tracker, int x, int y, long eventTime, Key key) {
- mIsPossibleGesture = false;
- // A gesture should start only from the letter key.
- if (GESTURE_ON && mIsAlphabetKeyboard && key != null && Keyboard.isLetterCode(key.mCode)) {
- mIsPossibleGesture = true;
- addPointToStroke(x, y, 0, tracker.mPointerId, false);
- }
- }
-
- public void onMoveEvent(PointerTracker tracker, int x, int y, long eventTime,
- boolean isHistorical, Key key) {
- final int gestureTime = (int)(eventTime - tracker.getDownTime());
- if (GESTURE_ON && mIsPossibleGesture) {
- final GestureStroke stroke = addPointToStroke(x, y, gestureTime, tracker.mPointerId,
- isHistorical);
- if (!isInGesture() && stroke.isStartOfAGesture(gestureTime)) {
- startBatchInput();
- }
- }
-
- if (key != null && isInGesture()) {
- final InputPointers batchPoints = getIncrementalBatchPoints();
- if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
- if (DEBUG_LISTENER) {
- Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
- }
- mSuggestions = mListener.onUpdateBatchInput(batchPoints);
- }
- }
- }
-
- public void onUpEvent(PointerTracker tracker, int x, int y, long eventTime) {
- if (isInGesture()) {
- final InputPointers batchPoints = getAllBatchPoints();
- if (DEBUG_LISTENER) {
- Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
- }
- mSuggestions = mListener.onUpdateBatchInput(batchPoints);
- }
- }
-
- private GestureStroke addPointToStroke(int x, int y, int time, int pointerId,
- boolean isHistorical) {
- GestureStroke stroke = mGestureStrokes.get(pointerId);
- if (stroke == null) {
- stroke = new GestureStroke(pointerId);
- mGestureStrokes.put(pointerId, stroke);
- }
- stroke.addPoint(x, y, time, isHistorical);
- return stroke;
- }
-
- // The working and return object of the following methods, {@link #getIncrementalBatchPoints()}
- // and {@link #getAllBatchPoints()}.
- private final InputPointers mAggregatedPointers = new InputPointers();
-
- private InputPointers getIncrementalBatchPoints() {
- final InputPointers pointers = mAggregatedPointers;
- pointers.reset();
- final int strokeSize = mGestureStrokes.size();
- for (int index = 0; index < strokeSize; index++) {
- final GestureStroke stroke = mGestureStrokes.valueAt(index);
- stroke.appendIncrementalBatchPoints(pointers);
- }
- return pointers;
- }
-
- private InputPointers getAllBatchPoints() {
- final InputPointers pointers = mAggregatedPointers;
- pointers.reset();
- final int strokeSize = mGestureStrokes.size();
- for (int index = 0; index < strokeSize; index++) {
- final GestureStroke stroke = mGestureStrokes.valueAt(index);
- stroke.appendAllBatchPoints(pointers);
- }
- return pointers;
- }
-
- private void clearBatchInputPoints() {
- final int strokeSize = mGestureStrokes.size();
- for (int index = 0; index < strokeSize; index++) {
- final GestureStroke stroke = mGestureStrokes.valueAt(index);
- stroke.reset();
- }
- mLastRecognitionPointSize = 0;
- mLastRecognitionTime = 0;
- }
-
- private boolean updateBatchInputRecognitionState(long eventTime, int size) {
- if (size > mLastRecognitionPointSize
- && eventTime > mLastRecognitionTime + MIN_RECOGNITION_TIME) {
- mLastRecognitionPointSize = size;
- mLastRecognitionTime = eventTime;
- return true;
- }
- return false;
- }
-
- private static class GestureStroke {
- private final int mPointerId;
- private final InputPointers mInputPointers = new InputPointers();
- private float mLength;
- private float mAngle;
- private int mIncrementalRecognitionPoint;
- private boolean mHasSharpCorner;
- private long mLastPointTime;
- private int mLastPointX;
- private int mLastPointY;
-
- private static int sMinGestureLength;
- private static int sSquaredGestureSampleLength;
-
- private static final float DOUBLE_PI = (float)(2 * Math.PI);
-
- public static void setGestureSampleLength(final int minGestureLength,
- final int sampleLength) {
- sMinGestureLength = minGestureLength;
- sSquaredGestureSampleLength = sampleLength * sampleLength;
- }
-
- public GestureStroke(int pointerId) {
- mPointerId = pointerId;
- reset();
- }
-
- public boolean isStartOfAGesture(int downDuration) {
- return downDuration > MIN_GESTURE_DURATION / 2 && mLength > sMinGestureLength / 2;
- }
-
- public void reset() {
- mLength = 0;
- mAngle = 0;
- mIncrementalRecognitionPoint = 0;
- mHasSharpCorner = false;
- mLastPointTime = 0;
- mInputPointers.reset();
- }
-
- private void updateLastPoint(final int x, final int y, final int time) {
- mLastPointTime = time;
- mLastPointX = x;
- mLastPointY = y;
- }
-
- public void addPoint(final int x, final int y, final int time, final boolean isHistorical) {
- final int size = mInputPointers.getPointerSize();
- if (size == 0) {
- mInputPointers.addPointer(x, y, mPointerId, time);
- if (!isHistorical) {
- updateLastPoint(x, y, time);
- }
- return;
- }
-
- final int[] xCoords = mInputPointers.getXCoordinates();
- final int[] yCoords = mInputPointers.getYCoordinates();
- final int lastX = xCoords[size - 1];
- final int lastY = yCoords[size - 1];
- final float dist = squaredDistance(lastX, lastY, x, y);
- if (dist > sSquaredGestureSampleLength) {
- mInputPointers.addPointer(x, y, mPointerId, time);
- mLength += dist;
- final float angle = angle(lastX, lastY, x, y);
- if (size > 1) {
- float curvature = getAngleDiff(angle, mAngle);
- if (curvature > GESTURE_RECOG_CURVATURE_THRESHOLD) {
- if (size > mIncrementalRecognitionPoint) {
- mIncrementalRecognitionPoint = size;
- }
- mHasSharpCorner = true;
- }
- if (!mHasSharpCorner) {
- mIncrementalRecognitionPoint = size;
- }
- }
- mAngle = angle;
- }
-
- if (!isHistorical) {
- final int duration = (int)(time - mLastPointTime);
- if (mLastPointTime != 0 && duration > 0) {
- final int squaredDuration = duration * duration;
- final float squaredSpeed =
- squaredDistance(mLastPointX, mLastPointY, x, y) / squaredDuration;
- if (squaredSpeed < SQUARED_GESTURE_RECOG_SPEED_THRESHOLD) {
- mIncrementalRecognitionPoint = size;
- }
- }
- updateLastPoint(x, y, time);
- }
- }
-
- private float getAngleDiff(float a1, float a2) {
- final float diff = Math.abs(a1 - a2);
- if (diff > Math.PI) {
- return DOUBLE_PI - diff;
- }
- return diff;
- }
-
- public void appendAllBatchPoints(InputPointers out) {
- out.append(mInputPointers, 0, mInputPointers.getPointerSize());
- }
-
- public void appendIncrementalBatchPoints(InputPointers out) {
- out.append(mInputPointers, 0, mIncrementalRecognitionPoint);
- }
- }
-
- static float squaredDistance(int p1x, int p1y, int p2x, int p2y) {
- final float dx = p1x - p2x;
- final float dy = p1y - p2y;
- return dx * dx + dy * dy;
- }
-
- static float angle(int p1x, int p1y, int p2x, int p2y) {
- final int dx = p1x - p2x;
- final int dy = p1y - p2y;
- if (dx == 0 && dy == 0) return 0;
- return (float)Math.atan2(dy, dx);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index e79db367c..1242967ad 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -19,6 +19,13 @@ package com.android.inputmethod.latin;
import android.view.inputmethod.EditorInfo;
public final class Constants {
+ public static final class Color {
+ /**
+ * The alpha value for fully opaque.
+ */
+ public final static int ALPHA_OPAQUE = 255;
+ }
+
public static final class ImeOption {
/**
* The private IME option used to indicate that no microphone should be shown for a given
diff --git a/java/src/com/android/inputmethod/latin/ImfUtils.java b/java/src/com/android/inputmethod/latin/ImfUtils.java
index b882a4860..1461c0240 100644
--- a/java/src/com/android/inputmethod/latin/ImfUtils.java
+++ b/java/src/com/android/inputmethod/latin/ImfUtils.java
@@ -90,6 +90,13 @@ public class ImfUtils {
return false;
}
+ public static InputMethodSubtype getCurrentInputMethodSubtype(Context context,
+ InputMethodSubtype defaultSubtype) {
+ final InputMethodManager imm = getInputMethodManager(context);
+ final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype();
+ return (currentSubtype != null) ? currentSubtype : defaultSubtype;
+ }
+
public static boolean hasMultipleEnabledIMEsOrSubtypes(Context context,
final boolean shouldIncludeAuxiliarySubtypes) {
final InputMethodManager imm = getInputMethodManager(context);
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index 9c32f947c..e561f5956 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -63,7 +63,7 @@ public class InputAttributes {
final boolean flagAutoComplete =
0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
- // Make sure that passwords are not displayed in {@link SuggestionsView}.
+ // Make sure that passwords are not displayed in {@link SuggestionStripView}.
if (InputTypeUtils.isPasswordInputType(inputType)
|| InputTypeUtils.isVisiblePasswordInputType(inputType)
|| InputTypeUtils.isEmailVariation(variation)
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
index 5ad53480f..cbc916a7e 100644
--- a/java/src/com/android/inputmethod/latin/InputPointers.java
+++ b/java/src/com/android/inputmethod/latin/InputPointers.java
@@ -16,14 +16,21 @@
package com.android.inputmethod.latin;
-import java.util.Arrays;
-
// TODO: This class is not thread-safe.
public class InputPointers {
- private final ScalableIntArray mXCoordinates = new ScalableIntArray();
- private final ScalableIntArray mYCoordinates = new ScalableIntArray();
- private final ScalableIntArray mPointerIds = new ScalableIntArray();
- private final ScalableIntArray mTimes = new ScalableIntArray();
+ private final int mDefaultCapacity;
+ private final ResizableIntArray mXCoordinates;
+ private final ResizableIntArray mYCoordinates;
+ private final ResizableIntArray mPointerIds;
+ private final ResizableIntArray mTimes;
+
+ public InputPointers(int defaultCapacity) {
+ mDefaultCapacity = defaultCapacity;
+ mXCoordinates = new ResizableIntArray(defaultCapacity);
+ mYCoordinates = new ResizableIntArray(defaultCapacity);
+ mPointerIds = new ResizableIntArray(defaultCapacity);
+ mTimes = new ResizableIntArray(defaultCapacity);
+ }
public void addPointer(int index, int x, int y, int pointerId, int time) {
mXCoordinates.add(index, x);
@@ -60,17 +67,42 @@ public class InputPointers {
* @param length the number of pointers to be appended.
*/
public void append(InputPointers src, int startPos, int length) {
+ if (length == 0) {
+ return;
+ }
mXCoordinates.append(src.mXCoordinates, startPos, length);
mYCoordinates.append(src.mYCoordinates, startPos, length);
mPointerIds.append(src.mPointerIds, startPos, length);
mTimes.append(src.mTimes, startPos, length);
}
+ /**
+ * Append the times, x-coordinates and y-coordinates in the specified {@link ResizableIntArray}
+ * to the end of this.
+ * @param pointerId the pointer id of the source.
+ * @param times the source {@link ResizableIntArray} to read the event times from.
+ * @param xCoordinates the source {@link ResizableIntArray} to read the x-coordinates from.
+ * @param yCoordinates the source {@link ResizableIntArray} to read the y-coordinates from.
+ * @param startPos the starting index of the data in {@code times} and etc.
+ * @param length the number of data to be appended.
+ */
+ public void append(int pointerId, ResizableIntArray times, ResizableIntArray xCoordinates,
+ ResizableIntArray yCoordinates, int startPos, int length) {
+ if (length == 0) {
+ return;
+ }
+ mXCoordinates.append(xCoordinates, startPos, length);
+ mYCoordinates.append(yCoordinates, startPos, length);
+ mPointerIds.fill(pointerId, startPos, length);
+ mTimes.append(times, startPos, length);
+ }
+
public void reset() {
- mXCoordinates.reset();
- mYCoordinates.reset();
- mPointerIds.reset();
- mTimes.reset();
+ final int defaultCapacity = mDefaultCapacity;
+ mXCoordinates.reset(defaultCapacity);
+ mYCoordinates.reset(defaultCapacity);
+ mPointerIds.reset(defaultCapacity);
+ mTimes.reset(defaultCapacity);
}
public int getPointerSize() {
@@ -92,73 +124,4 @@ public class InputPointers {
public int[] getTimes() {
return mTimes.getPrimitiveArray();
}
-
- private static class ScalableIntArray {
- private static final int DEFAULT_SIZE = BinaryDictionary.MAX_WORD_LENGTH;
- private int[] mArray;
- private int mLength;
-
- public ScalableIntArray() {
- reset();
- }
-
- public void add(int index, int val) {
- if (mLength < index + 1) {
- mLength = index;
- add(val);
- } else {
- mArray[index] = val;
- }
- }
-
- public void add(int val) {
- final int nextLength = mLength + 1;
- ensureCapacity(nextLength);
- mArray[mLength] = val;
- mLength = nextLength;
- }
-
- private void ensureCapacity(int minimumCapacity) {
- if (mArray.length < minimumCapacity) {
- final int nextCapacity = mArray.length * 2;
- // The following is the same as newLength = Math.max(minimumCapacity, nextCapacity);
- final int newLength = minimumCapacity > nextCapacity
- ? minimumCapacity
- : nextCapacity;
- mArray = Arrays.copyOf(mArray, newLength);
- }
- }
-
- public int getLength() {
- return mLength;
- }
-
- public void reset() {
- mArray = new int[DEFAULT_SIZE];
- mLength = 0;
- }
-
- public int[] getPrimitiveArray() {
- return mArray;
- }
-
- public void set(ScalableIntArray ip) {
- mArray = ip.mArray;
- mLength = ip.mLength;
- }
-
- public void copy(ScalableIntArray ip) {
- ensureCapacity(ip.mLength);
- System.arraycopy(ip.mArray, 0, mArray, 0, ip.mLength);
- mLength = ip.mLength;
- }
-
- public void append(ScalableIntArray src, int startPos, int length) {
- final int currentLength = mLength;
- final int newLength = currentLength + length;
- ensureCapacity(newLength);
- System.arraycopy(src.mArray, startPos, mArray, currentLength, length);
- mLength = newLength;
- }
- }
}
diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java
index 0dcb811b5..c15f45345 100644
--- a/java/src/com/android/inputmethod/latin/InputView.java
+++ b/java/src/com/android/inputmethod/latin/InputView.java
@@ -24,7 +24,7 @@ import android.view.View;
import android.widget.LinearLayout;
public class InputView extends LinearLayout {
- private View mSuggestionsContainer;
+ private View mSuggestionStripContainer;
private View mKeyboardView;
private int mKeyboardTopPadding;
@@ -43,13 +43,13 @@ public class InputView extends LinearLayout {
@Override
protected void onFinishInflate() {
- mSuggestionsContainer = findViewById(R.id.suggestions_container);
+ mSuggestionStripContainer = findViewById(R.id.suggestions_container);
mKeyboardView = findViewById(R.id.keyboard_view);
}
@Override
public boolean dispatchTouchEvent(MotionEvent me) {
- if (mSuggestionsContainer.getVisibility() == VISIBLE
+ if (mSuggestionStripContainer.getVisibility() == VISIBLE
&& mKeyboardView.getVisibility() == VISIBLE
&& forwardTouchEvent(me)) {
return true;
@@ -57,7 +57,8 @@ public class InputView extends LinearLayout {
return super.dispatchTouchEvent(me);
}
- // The touch events that hit the top padding of keyboard should be forwarded to SuggestionsView.
+ // The touch events that hit the top padding of keyboard should be forwarded to
+ // {@link SuggestionStripView}.
private boolean forwardTouchEvent(MotionEvent me) {
final Rect rect = mInputViewRect;
this.getGlobalVisibleRect(rect);
@@ -96,7 +97,7 @@ public class InputView extends LinearLayout {
}
final Rect receivingRect = mEventReceivingRect;
- mSuggestionsContainer.getGlobalVisibleRect(receivingRect);
+ mSuggestionStripContainer.getGlobalVisibleRect(receivingRect);
final int translatedX = x - receivingRect.left;
final int translatedY;
if (y < forwardingLimitY) {
@@ -106,7 +107,7 @@ public class InputView extends LinearLayout {
translatedY = y - receivingRect.top;
}
me.setLocation(translatedX, translatedY);
- mSuggestionsContainer.dispatchTouchEvent(me);
+ mSuggestionStripContainer.dispatchTouchEvent(me);
return true;
}
}
diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java
index 974af2584..bb39ce4f7 100644
--- a/java/src/com/android/inputmethod/latin/LastComposedWord.java
+++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java
@@ -45,7 +45,7 @@ public class LastComposedWord {
public final String mCommittedWord;
public final int mSeparatorCode;
public final CharSequence mPrevWord;
- public final InputPointers mInputPointers = new InputPointers();
+ public final InputPointers mInputPointers = new InputPointers(BinaryDictionary.MAX_WORD_LENGTH);
private boolean mActive;
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 518bcd5ce..d4b59c4cd 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -20,6 +20,7 @@ import static com.android.inputmethod.latin.Constants.ImeOption.FORCE_ASCII;
import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE;
import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT;
+import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -38,7 +39,6 @@ import android.os.Debug;
import android.os.IBinder;
import android.os.Message;
import android.os.SystemClock;
-import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.text.InputType;
import android.text.TextUtils;
@@ -67,10 +67,11 @@ import com.android.inputmethod.keyboard.KeyboardActionListener;
import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.KeyboardView;
-import com.android.inputmethod.keyboard.LatinKeyboardView;
+import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
import com.android.inputmethod.latin.define.ProductionFlag;
-import com.android.inputmethod.latin.suggestions.SuggestionsView;
+import com.android.inputmethod.latin.suggestions.SuggestionStripView;
+import com.android.inputmethod.research.ResearchLogger;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -81,7 +82,7 @@ import java.util.Locale;
* Input method implementation for Qwerty'ish keyboard.
*/
public class LatinIME extends InputMethodService implements KeyboardActionListener,
- SuggestionsView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener {
+ SuggestionStripView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener {
private static final String TAG = LatinIME.class.getSimpleName();
private static final boolean TRACE = false;
private static boolean DEBUG;
@@ -125,7 +126,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private View mExtractArea;
private View mKeyPreviewBackingView;
private View mSuggestionsContainer;
- private SuggestionsView mSuggestionsView;
+ private SuggestionStripView mSuggestionStripView;
/* package for tests */ Suggest mSuggest;
private CompletionInfo[] mApplicationSpecifiedCompletions;
private ApplicationInfo mTargetApplicationInfo;
@@ -205,7 +206,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
switch (msg.what) {
case MSG_UPDATE_SUGGESTION_STRIP:
- latinIme.updateSuggestionsOrPredictions();
+ latinIme.updateSuggestionStrip();
break;
case MSG_UPDATE_SHIFT_STATE:
switcher.updateShiftState();
@@ -548,9 +549,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
.findViewById(android.R.id.extractArea);
mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing);
mSuggestionsContainer = view.findViewById(R.id.suggestions_container);
- mSuggestionsView = (SuggestionsView) view.findViewById(R.id.suggestions_view);
- if (mSuggestionsView != null)
- mSuggestionsView.setListener(this, view);
+ mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
+ if (mSuggestionStripView != null)
+ mSuggestionStripView.setListener(this, view);
if (LatinImeLogger.sVISUALDEBUG) {
mKeyPreviewBackingView.setBackgroundColor(0x10FF0000);
}
@@ -597,7 +598,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) {
super.onStartInputView(editorInfo, restarting);
final KeyboardSwitcher switcher = mKeyboardSwitcher;
- LatinKeyboardView inputView = switcher.getKeyboardView();
+ MainKeyboardView inputView = switcher.getKeyboardView();
if (editorInfo == null) {
Log.e(TAG, "Null EditorInfo in onStartInputView()");
@@ -618,7 +619,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
+ ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0));
}
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.getInstance().start();
ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, mPrefs);
}
if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) {
@@ -674,8 +674,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
switcher.loadKeyboard(editorInfo, mCurrentSettings);
- if (mSuggestionsView != null)
- mSuggestionsView.clear();
+ if (mSuggestionStripView != null)
+ mSuggestionStripView.clear();
setSuggestionStripShownInternal(
isSuggestionsStripVisible(), /* needsInputViewShown */ false);
@@ -711,7 +711,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
LatinImeLogger.commit();
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.getInstance().stop();
+ ResearchLogger.getInstance().latinIME_onFinishInputInternal();
}
KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
@@ -862,7 +862,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return;
mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
if (applicationSpecifiedCompletions == null) {
- clearSuggestions();
+ clearSuggestionStrip();
return;
}
@@ -878,7 +878,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
false /* isPrediction */);
// When in fullscreen mode, show completions generated by the application
final boolean isAutoCorrection = false;
- setSuggestions(suggestedWords, isAutoCorrection);
+ setSuggestionStrip(suggestedWords, isAutoCorrection);
setAutoCorrectionIndicator(isAutoCorrection);
// TODO: is this the right thing to do? What should we auto-correct to in
// this case? This says to keep whatever the user typed.
@@ -889,7 +889,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) {
// TODO: Modify this if we support suggestions with hard keyboard
if (onEvaluateInputViewShown() && mSuggestionsContainer != null) {
- final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
+ final MainKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
final boolean inputViewShown = (keyboardView != null) ? keyboardView.isShown() : false;
final boolean shouldShowSuggestions = shown
&& (needsInputViewShown ? inputViewShown : true);
@@ -927,7 +927,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
- keyboardHeight;
final LayoutParams params = mKeyPreviewBackingView.getLayoutParams();
- params.height = mSuggestionsView.setMoreSuggestionsHeight(remainingHeight);
+ params.height = mSuggestionStripView.setMoreSuggestionsHeight(remainingHeight);
mKeyPreviewBackingView.setLayoutParams(params);
return params.height;
}
@@ -950,7 +950,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final int extraHeight = extractHeight + backingHeight + suggestionsHeight;
int touchY = extraHeight;
// Need to set touchable region only if input view is being shown
- final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
+ final MainKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
if (keyboardView != null && keyboardView.isShown()) {
if (mSuggestionsContainer.getVisibility() == View.VISIBLE) {
touchY -= suggestionsHeight;
@@ -988,7 +988,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// the composing word, reset the last composed word, tell the inputconnection about it.
private void resetEntireInputState() {
resetComposingState(true /* alsoResetLastComposedWord */);
- clearSuggestions();
+ clearSuggestionStrip();
mConnection.finishComposingText();
}
@@ -1011,7 +1011,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(),
separatorCode, prevWord);
}
- updateSuggestionsOrPredictions();
+ updateSuggestionStrip();
}
// Called from the KeyboardSwitcher which needs to know auto caps state to display
@@ -1091,7 +1091,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|| codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET;
}
- // Callback for the SuggestionsView, to call when the "add to dictionary" hint is pressed.
+ // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is
+ // pressed.
@Override
public boolean addWordToUserDictionary(String word) {
mUserDictionary.addWordToUserDictionary(word, 128);
@@ -1329,13 +1330,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
@Override
- public SuggestedWords onUpdateBatchInput(InputPointers batchPointers) {
+ public void onUpdateBatchInput(InputPointers batchPointers) {
mWordComposer.setBatchInputPointers(batchPointers);
- return updateSuggestionsOrPredictions();
+ final SuggestedWords suggestedWords = getSuggestedWords();
+ showSuggestionStrip(suggestedWords, null);
}
@Override
- public void onEndBatchInput(CharSequence text) {
+ public void onEndBatchInput(InputPointers batchPointers) {
+ mWordComposer.setBatchInputPointers(batchPointers);
+ final SuggestedWords suggestedWords = getSuggestedWords();
+ showSuggestionStrip(suggestedWords, null);
+ if (suggestedWords == null || suggestedWords.size() == 0) {
+ return;
+ }
+ final CharSequence text = suggestedWords.getWord(0);
+ if (TextUtils.isEmpty(text)) {
+ return;
+ }
mWordComposer.setBatchInputWord(text);
mConnection.beginBatchEdit();
if (SPACE_STATE_PHANTOM == mSpaceState) {
@@ -1555,7 +1567,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mSpaceState = SPACE_STATE_WEAK;
}
// In case the "add to dictionary" hint was still displayed.
- if (null != mSuggestionsView) mSuggestionsView.dismissAddToDictionaryHint();
+ if (null != mSuggestionStripView) mSuggestionStripView.dismissAddToDictionaryHint();
}
mHandler.postUpdateSuggestionStrip();
Utils.Stats.onNonSeparator((char)primaryCode, x, y);
@@ -1634,7 +1646,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void handleClose() {
commitTyped(LastComposedWord.NOT_A_SEPARATOR);
requestHideSelf(0);
- LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+ MainKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
if (inputView != null)
inputView.closing();
}
@@ -1642,14 +1654,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// TODO: make this private
// Outside LatinIME, only used by the test suite.
/* package for tests */ boolean isShowingPunctuationList() {
- if (mSuggestionsView == null) return false;
- return mCurrentSettings.mSuggestPuncList == mSuggestionsView.getSuggestions();
+ if (mSuggestionStripView == null) return false;
+ return mCurrentSettings.mSuggestPuncList == mSuggestionStripView.getSuggestions();
}
private boolean isSuggestionsStripVisible() {
- if (mSuggestionsView == null)
+ if (mSuggestionStripView == null)
return false;
- if (mSuggestionsView.isShowingAddToDictionaryHint())
+ if (mSuggestionStripView.isShowingAddToDictionaryHint())
return true;
if (!mCurrentSettings.isSuggestionStripVisibleInOrientation(mDisplayOrientation))
return false;
@@ -1658,14 +1670,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return mCurrentSettings.isSuggestionsRequested(mDisplayOrientation);
}
- private void clearSuggestions() {
- setSuggestions(SuggestedWords.EMPTY, false);
+ private void clearSuggestionStrip() {
+ setSuggestionStrip(SuggestedWords.EMPTY, false);
setAutoCorrectionIndicator(false);
}
- private void setSuggestions(final SuggestedWords words, final boolean isAutoCorrection) {
- if (mSuggestionsView != null) {
- mSuggestionsView.setSuggestions(words);
+ private void setSuggestionStrip(final SuggestedWords words, final boolean isAutoCorrection) {
+ if (mSuggestionStripView != null) {
+ mSuggestionStripView.setSuggestions(words);
mKeyboardSwitcher.onAutoCorrectionStateChanged(isAutoCorrection);
}
}
@@ -1681,8 +1693,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
- // TODO: rename this method to updateSuggestionStrip or simply updateSuggestions
- private SuggestedWords updateSuggestionsOrPredictions() {
+ private void updateSuggestionStrip() {
mHandler.cancelUpdateSuggestionStrip();
// Check if we have a suggestion engine attached.
@@ -1692,15 +1703,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
+ "requested!");
mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
}
- return null;
+ return;
}
- final String typedWord = mWordComposer.getTypedWord();
if (!mWordComposer.isComposingWord() && !mCurrentSettings.mBigramPredictionEnabled) {
setPunctuationSuggestions();
- return null;
+ return;
}
+ final SuggestedWords suggestedWords = getSuggestedWords();
+ final String typedWord = mWordComposer.getTypedWord();
+ showSuggestionStrip(suggestedWords, typedWord);
+ }
+
+ private SuggestedWords getSuggestedWords() {
+ final String typedWord = mWordComposer.getTypedWord();
// Get the word on which we should search the bigrams. If we are composing a word, it's
// whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we
// should just skip whitespace if any, so 1.
@@ -1708,13 +1725,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final CharSequence prevWord =
mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators,
mWordComposer.isComposingWord() ? 2 : 1);
- SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
+ final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(),
mCurrentSettings.mCorrectionEnabled);
- suggestedWords = maybeRetrieveOlderSuggestions(typedWord, suggestedWords);
-
- showSuggestions(suggestedWords, typedWord);
- return suggestedWords;
+ return maybeRetrieveOlderSuggestions(typedWord, suggestedWords);
}
private SuggestedWords maybeRetrieveOlderSuggestions(final CharSequence typedWord,
@@ -1728,10 +1742,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// revert to suggestions - although it is unclear how we can come here if it's displayed.
if (suggestedWords.size() > 1 || typedWord.length() <= 1
|| !suggestedWords.mTypedWordValid
- || mSuggestionsView.isShowingAddToDictionaryHint()) {
+ || mSuggestionStripView.isShowingAddToDictionaryHint()) {
return suggestedWords;
} else {
- SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions();
+ SuggestedWords previousSuggestions = mSuggestionStripView.getSuggestions();
if (previousSuggestions == mCurrentSettings.mSuggestPuncList) {
previousSuggestions = SuggestedWords.EMPTY;
}
@@ -1747,10 +1761,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
- private void showSuggestions(final SuggestedWords suggestedWords,
+ private void showSuggestionStrip(final SuggestedWords suggestedWords,
final CharSequence typedWord) {
if (null == suggestedWords || suggestedWords.size() <= 0) {
- clearSuggestions();
+ clearSuggestionStrip();
return;
}
final CharSequence autoCorrection;
@@ -1765,7 +1779,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
mWordComposer.setAutoCorrection(autoCorrection);
final boolean isAutoCorrection = suggestedWords.willAutoCorrect();
- setSuggestions(suggestedWords, isAutoCorrection);
+ setSuggestionStrip(suggestedWords, isAutoCorrection);
setAutoCorrectionIndicator(isAutoCorrection);
setSuggestionStripShown(isSuggestionsStripVisible());
}
@@ -1773,7 +1787,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void commitCurrentAutoCorrection(final int separatorCodePoint) {
// Complete any pending suggestions query first
if (mHandler.hasPendingUpdateSuggestions()) {
- updateSuggestionsOrPredictions();
+ updateSuggestionStrip();
}
final CharSequence typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull();
final String typedWord = mWordComposer.getTypedWord();
@@ -1802,11 +1816,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
- // Called from SuggestionsView through the SuggestionsView.Listener interface
+ // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
+ // interface
@Override
public void pickSuggestionManually(final int index, final CharSequence suggestion,
final int x, final int y) {
- final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
+ final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions();
// If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
if (suggestion.length() == 1 && isShowingPunctuationList()) {
// Word separators are suggested before the user inputs something.
@@ -1838,8 +1853,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mCurrentSettings.isApplicationSpecifiedCompletionsOn()
&& mApplicationSpecifiedCompletions != null
&& index >= 0 && index < mApplicationSpecifiedCompletions.length) {
- if (mSuggestionsView != null) {
- mSuggestionsView.clear();
+ if (mSuggestionStripView != null) {
+ mSuggestionStripView.clear();
}
mKeyboardSwitcher.updateShiftState();
resetComposingState(true /* alsoResetLastComposedWord */);
@@ -1872,23 +1887,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mKeyboardSwitcher.updateShiftState();
// We should show the "Touch again to save" hint if the user pressed the first entry
- // AND either:
- // - There is no dictionary (we know that because we tried to load it => null != mSuggest
- // AND mSuggest.hasMainDictionary() is false)
- // - There is a dictionary and the word is not in it
+ // AND it's in none of our current dictionaries (main, user or otherwise).
// Please note that if mSuggest is null, it means that everything is off: suggestion
// and correction, so we shouldn't try to show the hint
final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null
- // If there is no dictionary the hint should be shown.
- && (!mSuggest.hasMainDictionary()
- // If "suggestion" is not in the dictionary, the hint should be shown.
- || !AutoCorrection.isValidWord(
- mSuggest.getUnigramDictionaries(), suggestion, true));
+ // If the suggestion is not in the dictionary, the hint should be shown.
+ && !AutoCorrection.isValidWord(mSuggest.getUnigramDictionaries(), suggestion, true);
Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE, WordComposer.NOT_A_COORDINATE,
WordComposer.NOT_A_COORDINATE);
if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) {
- mSuggestionsView.showAddToDictionaryHint(suggestion, mCurrentSettings.mHintToSaveText);
+ mSuggestionStripView.showAddToDictionaryHint(
+ suggestion, mCurrentSettings.mHintToSaveText);
} else {
// If we're not showing the "Touch again to save", then update the suggestion strip.
mHandler.postUpdateSuggestionStrip();
@@ -1900,7 +1910,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
*/
private void commitChosenWord(final CharSequence chosenWord, final int commitType,
final int separatorCode) {
- final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
+ final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions();
mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1);
if (ProductionFlag.IS_EXPERIMENTAL) {
@@ -1918,9 +1928,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void setPunctuationSuggestions() {
if (mCurrentSettings.mBigramPredictionEnabled) {
- clearSuggestions();
+ clearSuggestionStrip();
} else {
- setSuggestions(mCurrentSettings.mSuggestPuncList, false);
+ setSuggestionStrip(mCurrentSettings.mSuggestPuncList, false);
}
setAutoCorrectionIndicator(false);
setSuggestionStripShown(isSuggestionsStripVisible());
@@ -2102,18 +2112,26 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
};
private void launchSettings() {
- launchSettingsClass(SettingsActivity.class);
+ handleClose();
+ launchSubActivity(SettingsActivity.class);
}
// Called from debug code only
public void launchDebugSettings() {
- launchSettingsClass(DebugSettingsActivity.class);
+ handleClose();
+ launchSubActivity(DebugSettingsActivity.class);
}
- private void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) {
- handleClose();
+ public void launchKeyboardedDialogActivity(Class<? extends Activity> activityClass) {
+ // Put the text in the attached EditText into a safe, saved state before switching to a
+ // new activity that will also use the soft keyboard.
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+ launchSubActivity(activityClass);
+ }
+
+ private void launchSubActivity(Class<? extends Activity> activityClass) {
Intent intent = new Intent();
- intent.setClass(LatinIME.this, settingsClass);
+ intent.setClass(LatinIME.this, activityClass);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
@@ -2151,7 +2169,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
showOptionDialog(builder.create());
}
- /* package */ void showOptionDialog(AlertDialog dialog) {
+ public void showOptionDialog(AlertDialog dialog) {
final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
if (windowToken == null) return;
diff --git a/java/src/com/android/inputmethod/latin/ResizableIntArray.java b/java/src/com/android/inputmethod/latin/ResizableIntArray.java
new file mode 100644
index 000000000..387d45a53
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/ResizableIntArray.java
@@ -0,0 +1,134 @@
+/*
+ * 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 java.util.Arrays;
+
+// TODO: This class is not thread-safe.
+public class ResizableIntArray {
+ private int[] mArray;
+ private int mLength;
+
+ public ResizableIntArray(final int capacity) {
+ reset(capacity);
+ }
+
+ public int get(final int index) {
+ if (index < mLength) {
+ return mArray[index];
+ }
+ throw new ArrayIndexOutOfBoundsException("length=" + mLength + "; index=" + index);
+ }
+
+ public void add(final int index, final int val) {
+ if (index < mLength) {
+ mArray[index] = val;
+ } else {
+ mLength = index;
+ add(val);
+ }
+ }
+
+ public void add(final int val) {
+ final int currentLength = mLength;
+ ensureCapacity(currentLength + 1);
+ mArray[currentLength] = val;
+ mLength = currentLength + 1;
+ }
+
+ /**
+ * Calculate the new capacity of {@code mArray}.
+ * @param minimumCapacity the minimum capacity that the {@code mArray} should have.
+ * @return the new capacity that the {@code mArray} should have. Returns zero when there is no
+ * need to expand {@code mArray}.
+ */
+ private int calculateCapacity(final int minimumCapacity) {
+ final int currentCapcity = mArray.length;
+ if (currentCapcity < minimumCapacity) {
+ final int nextCapacity = currentCapcity * 2;
+ // The following is the same as return Math.max(minimumCapacity, nextCapacity);
+ return minimumCapacity > nextCapacity ? minimumCapacity : nextCapacity;
+ }
+ return 0;
+ }
+
+ private void ensureCapacity(final int minimumCapacity) {
+ final int newCapacity = calculateCapacity(minimumCapacity);
+ if (newCapacity > 0) {
+ // TODO: Implement primitive array pool.
+ mArray = Arrays.copyOf(mArray, newCapacity);
+ }
+ }
+
+ public int getLength() {
+ return mLength;
+ }
+
+ public void setLength(final int newLength) {
+ ensureCapacity(newLength);
+ mLength = newLength;
+ }
+
+ public void reset(final int capacity) {
+ // TODO: Implement primitive array pool.
+ mArray = new int[capacity];
+ mLength = 0;
+ }
+
+ public int[] getPrimitiveArray() {
+ return mArray;
+ }
+
+ public void set(final ResizableIntArray ip) {
+ // TODO: Implement primitive array pool.
+ mArray = ip.mArray;
+ mLength = ip.mLength;
+ }
+
+ public void copy(final ResizableIntArray ip) {
+ final int newCapacity = calculateCapacity(ip.mLength);
+ if (newCapacity > 0) {
+ // TODO: Implement primitive array pool.
+ mArray = new int[newCapacity];
+ }
+ System.arraycopy(ip.mArray, 0, mArray, 0, ip.mLength);
+ mLength = ip.mLength;
+ }
+
+ public void append(final ResizableIntArray src, final int startPos, final int length) {
+ if (length == 0) {
+ return;
+ }
+ final int currentLength = mLength;
+ final int newLength = currentLength + length;
+ ensureCapacity(newLength);
+ System.arraycopy(src.mArray, startPos, mArray, currentLength, length);
+ mLength = newLength;
+ }
+
+ public void fill(final int value, final int startPos, final int length) {
+ if (startPos < 0 || length < 0) {
+ throw new IllegalArgumentException("startPos=" + startPos + "; length=" + length);
+ }
+ final int endPos = startPos + length;
+ ensureCapacity(endPos);
+ Arrays.fill(mArray, startPos, endPos, value);
+ if (mLength < endPos) {
+ mLength = endPos;
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 5786978a8..8b4c17322 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -28,6 +28,7 @@ import android.view.inputmethod.InputConnection;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import java.util.regex.Pattern;
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 70acdc771..45608f439 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -39,6 +39,7 @@ import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import com.android.inputmethodcommon.InputMethodSettingsFragment;
public class Settings extends InputMethodSettingsFragment
@@ -70,6 +71,7 @@ public class Settings extends InputMethodSettingsFragment
"pref_key_preview_popup_dismiss_delay";
public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction";
+ public static final String PREF_GESTURE_INPUT = "gesture_input";
public static final String PREF_VIBRATION_DURATION_SETTINGS =
"pref_vibration_duration_settings";
public static final String PREF_KEYPRESS_SOUND_VOLUME =
@@ -196,6 +198,12 @@ public class Settings extends InputMethodSettingsFragment
textCorrectionGroup.removePreference(dictionaryLink);
}
+ final boolean gestureInputEnabledByBuildConfig = res.getBoolean(
+ R.bool.config_gesture_input_enabled_by_build_config);
+ if (!gestureInputEnabledByBuildConfig) {
+ final Preference gestureInputPref = findPreference(PREF_GESTURE_INPUT);
+ miscSettings.removePreference(gestureInputPref);
+ }
final boolean showUsabilityStudyModeOption =
res.getBoolean(R.bool.config_enable_usability_study_mode_option)
|| ProductionFlag.IS_EXPERIMENTAL || ENABLE_EXPERIMENTAL_SETTINGS;
@@ -208,7 +216,8 @@ public class Settings extends InputMethodSettingsFragment
if (ProductionFlag.IS_EXPERIMENTAL) {
if (usabilityStudyPref instanceof CheckBoxPreference) {
CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref;
- checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE, true));
+ checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE,
+ ResearchLogger.DEFAULT_USABILITY_STUDY_MODE));
checkbox.setSummary(R.string.settings_warning_researcher_mode);
}
}
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index 10025daf8..3ed981375 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -83,6 +83,7 @@ public class SettingsValues {
@SuppressWarnings("unused") // TODO: Use this
private final float mKeypressSoundVolumeRawValue;
private final InputMethodSubtype[] mAdditionalSubtypes;
+ public final boolean mGestureInputEnabled;
// From the input box
private final InputAttributes mInputAttributes;
@@ -169,6 +170,10 @@ public class SettingsValues {
mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain);
mAdditionalSubtypes = AdditionalSubtype.createAdditionalSubtypesArray(
getPrefAdditionalSubtypes(prefs, res));
+ final boolean gestureInputEnabledByBuildConfig = res.getBoolean(
+ R.bool.config_gesture_input_enabled_by_build_config);
+ mGestureInputEnabled = gestureInputEnabledByBuildConfig
+ && prefs.getBoolean(Settings.PREF_GESTURE_INPUT, true);
mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect;
mSuggestionVisibility = createSuggestionVisibility(res);
}
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 664de6774..a7a5fcb5f 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -98,9 +98,9 @@ public class SubtypeSwitcher {
mConnectivityManager = (ConnectivityManager) service.getSystemService(
Context.CONNECTIVITY_SERVICE);
mCurrentSystemLocale = mResources.getConfiguration().locale;
- mCurrentSubtype = mImm.getCurrentInputMethodSubtype();
mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
service, SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
+ mCurrentSubtype = ImfUtils.getCurrentInputMethodSubtype(service, mNoLanguageSubtype);
if (mNoLanguageSubtype == null) {
throw new RuntimeException("Can't find no lanugage with QWERTY subtype");
}
@@ -113,7 +113,7 @@ public class SubtypeSwitcher {
// Only configuration changed event is allowed to call this because this is heavy.
private void updateAllParameters() {
mCurrentSystemLocale = mResources.getConfiguration().locale;
- updateSubtype(mImm.getCurrentInputMethodSubtype());
+ updateSubtype(ImfUtils.getCurrentInputMethodSubtype(mService, mNoLanguageSubtype));
updateParametersOnStartInputView();
}
@@ -142,7 +142,7 @@ public class SubtypeSwitcher {
+ currentSubtype.getLocale() + "/" + currentSubtype.getExtraValue());
Log.w(TAG, "Last subtype was disabled. Update to the current one.");
}
- updateSubtype(mImm.getCurrentInputMethodSubtype());
+ updateSubtype(ImfUtils.getCurrentInputMethodSubtype(mService, mNoLanguageSubtype));
}
}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index ca9dbaf05..6d346d179 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -33,7 +33,7 @@ public class WordComposer {
private static final int N = BinaryDictionary.MAX_WORD_LENGTH;
private int[] mPrimaryKeyCodes;
- private final InputPointers mInputPointers = new InputPointers();
+ private final InputPointers mInputPointers = new InputPointers(N);
private final StringBuilder mTypedWord;
private CharSequence mAutoCorrection;
private boolean mIsResumed;
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index c6fe43b69..58b01aa55 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -42,10 +42,10 @@ public class MoreSuggestions extends Keyboard {
private int mToPos;
public static class MoreSuggestionsParam extends Keyboard.Params {
- private final int[] mWidths = new int[SuggestionsView.MAX_SUGGESTIONS];
- private final int[] mRowNumbers = new int[SuggestionsView.MAX_SUGGESTIONS];
- private final int[] mColumnOrders = new int[SuggestionsView.MAX_SUGGESTIONS];
- private final int[] mNumColumnsInRow = new int[SuggestionsView.MAX_SUGGESTIONS];
+ private final int[] mWidths = new int[SuggestionStripView.MAX_SUGGESTIONS];
+ private final int[] mRowNumbers = new int[SuggestionStripView.MAX_SUGGESTIONS];
+ private final int[] mColumnOrders = new int[SuggestionStripView.MAX_SUGGESTIONS];
+ private final int[] mNumColumnsInRow = new int[SuggestionStripView.MAX_SUGGESTIONS];
private static final int MAX_COLUMNS_IN_ROW = 3;
private int mNumRows;
public Drawable mDivider;
@@ -63,7 +63,7 @@ public class MoreSuggestions extends Keyboard {
int row = 0;
int pos = fromPos, rowStartPos = fromPos;
- final int size = Math.min(suggestions.size(), SuggestionsView.MAX_SUGGESTIONS);
+ final int size = Math.min(suggestions.size(), SuggestionStripView.MAX_SUGGESTIONS);
while (pos < size) {
final String word = suggestions.getWord(pos).toString();
// TODO: Should take care of text x-scaling.
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 19287e3f3..5b23d7f3c 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -68,7 +68,7 @@ public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel {
@Override
public void onCodeInput(int primaryCode, int x, int y) {
final int index = primaryCode - MoreSuggestions.SUGGESTION_CODE_BASE;
- if (index >= 0 && index < SuggestionsView.MAX_SUGGESTIONS) {
+ if (index >= 0 && index < SuggestionStripView.MAX_SUGGESTIONS) {
mListener.onCustomRequest(index);
}
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index 4d33f4ba5..b57ffd2de 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -60,15 +60,15 @@ import com.android.inputmethod.keyboard.ViewLayoutUtils;
import com.android.inputmethod.latin.AutoCorrection;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.ResearchLogger;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.Utils;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import java.util.ArrayList;
-public class SuggestionsView extends RelativeLayout implements OnClickListener,
+public class SuggestionStripView extends RelativeLayout implements OnClickListener,
OnLongClickListener {
public interface Listener {
public boolean addWordToUserDictionary(String word);
@@ -98,24 +98,24 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
private Listener mListener;
private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
- private final SuggestionsViewParams mParams;
+ private final SuggestionStripViewParams mParams;
private static final float MIN_TEXT_XSCALE = 0.70f;
private final UiHandler mHandler = new UiHandler(this);
- private static class UiHandler extends StaticInnerHandlerWrapper<SuggestionsView> {
+ private static class UiHandler extends StaticInnerHandlerWrapper<SuggestionStripView> {
private static final int MSG_HIDE_PREVIEW = 0;
- public UiHandler(SuggestionsView outerInstance) {
+ public UiHandler(SuggestionStripView outerInstance) {
super(outerInstance);
}
@Override
public void dispatchMessage(Message msg) {
- final SuggestionsView suggestionsView = getOuterInstance();
+ final SuggestionStripView suggestionStripView = getOuterInstance();
switch (msg.what) {
case MSG_HIDE_PREVIEW:
- suggestionsView.hidePreview();
+ suggestionStripView.hidePreview();
break;
}
}
@@ -129,7 +129,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
}
}
- private static class SuggestionsViewParams {
+ private static class SuggestionStripViewParams {
private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3;
private static final int DEFAULT_CENTER_SUGGESTION_PERCENTILE = 40;
private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2;
@@ -175,7 +175,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
private final TextView mLeftwardsArrowView;
private final TextView mHintToSaveView;
- public SuggestionsViewParams(Context context, AttributeSet attrs, int defStyle,
+ public SuggestionStripViewParams(Context context, AttributeSet attrs, int defStyle,
ArrayList<TextView> words, ArrayList<View> dividers, ArrayList<TextView> infos) {
mWords = words;
mDividers = dividers;
@@ -191,38 +191,39 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
final Resources res = word.getResources();
mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height);
- final TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.SuggestionsView, defStyle, R.style.SuggestionsViewStyle);
- mSuggestionStripOption = a.getInt(R.styleable.SuggestionsView_suggestionStripOption, 0);
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle);
+ mSuggestionStripOption = a.getInt(
+ R.styleable.SuggestionStripView_suggestionStripOption, 0);
final float alphaValidTypedWord = getPercent(a,
- R.styleable.SuggestionsView_alphaValidTypedWord, 100);
+ R.styleable.SuggestionStripView_alphaValidTypedWord, 100);
final float alphaTypedWord = getPercent(a,
- R.styleable.SuggestionsView_alphaTypedWord, 100);
+ R.styleable.SuggestionStripView_alphaTypedWord, 100);
final float alphaAutoCorrect = getPercent(a,
- R.styleable.SuggestionsView_alphaAutoCorrect, 100);
+ R.styleable.SuggestionStripView_alphaAutoCorrect, 100);
final float alphaSuggested = getPercent(a,
- R.styleable.SuggestionsView_alphaSuggested, 100);
- mAlphaObsoleted = getPercent(a, R.styleable.SuggestionsView_alphaSuggested, 100);
- mColorValidTypedWord = applyAlpha(
- a.getColor(R.styleable.SuggestionsView_colorValidTypedWord, 0),
- alphaValidTypedWord);
- mColorTypedWord = applyAlpha(
- a.getColor(R.styleable.SuggestionsView_colorTypedWord, 0), alphaTypedWord);
- mColorAutoCorrect = applyAlpha(
- a.getColor(R.styleable.SuggestionsView_colorAutoCorrect, 0), alphaAutoCorrect);
- mColorSuggested = applyAlpha(
- a.getColor(R.styleable.SuggestionsView_colorSuggested, 0), alphaSuggested);
+ R.styleable.SuggestionStripView_alphaSuggested, 100);
+ mAlphaObsoleted = getPercent(a,
+ R.styleable.SuggestionStripView_alphaSuggested, 100);
+ mColorValidTypedWord = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorValidTypedWord, 0), alphaValidTypedWord);
+ mColorTypedWord = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorTypedWord, 0), alphaTypedWord);
+ mColorAutoCorrect = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorAutoCorrect, 0), alphaAutoCorrect);
+ mColorSuggested = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorSuggested, 0), alphaSuggested);
mSuggestionsCountInStrip = a.getInt(
- R.styleable.SuggestionsView_suggestionsCountInStrip,
+ R.styleable.SuggestionStripView_suggestionsCountInStrip,
DEFAULT_SUGGESTIONS_COUNT_IN_STRIP);
mCenterSuggestionWeight = getPercent(a,
- R.styleable.SuggestionsView_centerSuggestionPercentile,
+ R.styleable.SuggestionStripView_centerSuggestionPercentile,
DEFAULT_CENTER_SUGGESTION_PERCENTILE);
mMaxMoreSuggestionsRow = a.getInt(
- R.styleable.SuggestionsView_maxMoreSuggestionsRow,
+ R.styleable.SuggestionStripView_maxMoreSuggestionsRow,
DEFAULT_MAX_MORE_SUGGESTIONS_ROW);
mMinMoreSuggestionsWidth = getRatio(a,
- R.styleable.SuggestionsView_minMoreSuggestionsWidth);
+ R.styleable.SuggestionStripView_minMoreSuggestionsWidth);
a.recycle();
mMoreSuggestionsHint = getMoreSuggestionsHint(res,
@@ -596,15 +597,15 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
}
/**
- * Construct a {@link SuggestionsView} for showing suggestions to be picked by the user.
+ * Construct a {@link SuggestionStripView} for showing suggestions to be picked by the user.
* @param context
* @param attrs
*/
- public SuggestionsView(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.suggestionsViewStyle);
+ public SuggestionStripView(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.suggestionStripViewStyle);
}
- public SuggestionsView(Context context, AttributeSet attrs, int defStyle) {
+ public SuggestionStripView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final LayoutInflater inflater = LayoutInflater.from(context);
@@ -631,7 +632,8 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
mInfos.add((TextView)inflater.inflate(R.layout.suggestion_info, null));
}
- mParams = new SuggestionsViewParams(context, attrs, defStyle, mWords, mDividers, mInfos);
+ mParams = new SuggestionStripViewParams(
+ context, attrs, defStyle, mWords, mDividers, mInfos);
mMoreSuggestionsContainer = inflater.inflate(R.layout.more_suggestions, null);
mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer
@@ -677,7 +679,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
mSuggestedWords = suggestedWords;
mParams.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth());
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.suggestionsView_setSuggestions(mSuggestedWords);
+ ResearchLogger.suggestionStripView_setSuggestions(mSuggestedWords);
}
}
@@ -759,7 +761,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
}
private boolean showMoreSuggestions() {
- final SuggestionsViewParams params = mParams;
+ final SuggestionStripViewParams params = mParams;
if (params.mMoreSuggestionsAvailable) {
final int stripWidth = getWidth();
final View container = mMoreSuggestionsContainer;
diff --git a/java/src/com/android/inputmethod/research/FeedbackActivity.java b/java/src/com/android/inputmethod/research/FeedbackActivity.java
new file mode 100644
index 000000000..c9f3b476a
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/FeedbackActivity.java
@@ -0,0 +1,52 @@
+/*
+ * 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 android.app.Activity;
+import android.os.Bundle;
+import android.text.Editable;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import com.android.inputmethod.latin.R;
+
+public class FeedbackActivity extends Activity {
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.research_feedback_activity);
+ final FeedbackLayout layout = (FeedbackLayout) findViewById(R.id.research_feedback_layout);
+ layout.setActivity(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ public void onBackPressed() {
+ ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
+ super.onBackPressed();
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/FeedbackFragment.java b/java/src/com/android/inputmethod/research/FeedbackFragment.java
new file mode 100644
index 000000000..a2e08e2b7
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/FeedbackFragment.java
@@ -0,0 +1,73 @@
+/*
+ * 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 android.app.Activity;
+import android.app.Fragment;
+import android.os.Bundle;
+import android.text.Editable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import com.android.inputmethod.latin.R;
+
+public class FeedbackFragment extends Fragment {
+ private EditText mEditText;
+ private CheckBox mCheckBox;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ final View view = inflater.inflate(R.layout.research_feedback_fragment_layout, container,
+ false);
+ mEditText = (EditText) view.findViewById(R.id.research_feedback_contents);
+ mCheckBox = (CheckBox) view.findViewById(R.id.research_feedback_include_history);
+
+ final Button sendButton = (Button) view.findViewById(
+ R.id.research_feedback_send_button);
+ sendButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Editable editable = mEditText.getText();
+ final String feedbackContents = editable.toString();
+ final boolean includeHistory = mCheckBox.isChecked();
+ ResearchLogger.getInstance().sendFeedback(feedbackContents, includeHistory);
+ final Activity activity = FeedbackFragment.this.getActivity();
+ activity.finish();
+ ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
+ }
+ });
+
+ final Button cancelButton = (Button) view.findViewById(
+ R.id.research_feedback_cancel_button);
+ cancelButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Activity activity = FeedbackFragment.this.getActivity();
+ activity.finish();
+ ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
+ }
+ });
+
+ return view;
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/FeedbackLayout.java b/java/src/com/android/inputmethod/research/FeedbackLayout.java
new file mode 100644
index 000000000..f2cbfe308
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/FeedbackLayout.java
@@ -0,0 +1,62 @@
+/*
+ * 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 android.app.Activity;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.LinearLayout;
+
+public class FeedbackLayout extends LinearLayout {
+ private Activity mActivity;
+
+ public FeedbackLayout(Context context) {
+ super(context);
+ }
+
+ public FeedbackLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public FeedbackLayout(Context context, AttributeSet attrs, int defstyle) {
+ super(context, attrs, defstyle);
+ }
+
+ public void setActivity(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public boolean dispatchKeyEventPreIme(KeyEvent event) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+ KeyEvent.DispatcherState state = getKeyDispatcherState();
+ if (state != null) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN
+ && event.getRepeatCount() == 0) {
+ state.startTracking(event, this);
+ return true;
+ } else if (event.getAction() == KeyEvent.ACTION_UP
+ && !event.isCanceled() && state.isTracking(event)) {
+ mActivity.onBackPressed();
+ return true;
+ }
+ }
+ }
+ return super.dispatchKeyEventPreIme(event);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java
index 1de5cb36a..18bf3c07f 100644
--- a/java/src/com/android/inputmethod/latin/ResearchLog.java
+++ b/java/src/com/android/inputmethod/research/ResearchLog.java
@@ -14,7 +14,7 @@
* the License.
*/
-package com.android.inputmethod.latin;
+package com.android.inputmethod.research;
import android.content.SharedPreferences;
import android.os.SystemClock;
@@ -23,9 +23,10 @@ import android.util.Log;
import android.view.inputmethod.CompletionInfo;
import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.latin.ResearchLogger.LogUnit;
+import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger.LogUnit;
import java.io.BufferedWriter;
import java.io.File;
@@ -55,13 +56,14 @@ public class ResearchLog {
final ScheduledExecutorService mExecutor;
/* package */ final File mFile;
- private JsonWriter mJsonWriter = NULL_JSON_WRITER; // should never be null
+ private JsonWriter mJsonWriter = NULL_JSON_WRITER;
private int mLoggingState;
private static final int LOGGING_STATE_UNSTARTED = 0;
- private static final int LOGGING_STATE_RUNNING = 1;
- private static final int LOGGING_STATE_STOPPING = 2;
- private static final int LOGGING_STATE_STOPPED = 3;
+ private static final int LOGGING_STATE_READY = 1; // don't create file until necessary
+ private static final int LOGGING_STATE_RUNNING = 2;
+ private static final int LOGGING_STATE_STOPPING = 3;
+ private static final int LOGGING_STATE_STOPPED = 4;
private static final long FLUSH_DELAY_IN_MS = 1000 * 5;
private static class NullOutputStream extends OutputStream {
@@ -94,11 +96,9 @@ public class ResearchLog {
public synchronized void start() throws IOException {
switch (mLoggingState) {
case LOGGING_STATE_UNSTARTED:
- mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile)));
- mJsonWriter.setLenient(true);
- mJsonWriter.beginArray();
- mLoggingState = LOGGING_STATE_RUNNING;
+ mLoggingState = LOGGING_STATE_READY;
break;
+ case LOGGING_STATE_READY:
case LOGGING_STATE_RUNNING:
case LOGGING_STATE_STOPPING:
case LOGGING_STATE_STOPPED:
@@ -111,6 +111,7 @@ public class ResearchLog {
case LOGGING_STATE_UNSTARTED:
mLoggingState = LOGGING_STATE_STOPPED;
break;
+ case LOGGING_STATE_READY:
case LOGGING_STATE_RUNNING:
mExecutor.submit(new Callable<Object>() {
@Override
@@ -120,14 +121,13 @@ public class ResearchLog {
mJsonWriter.flush();
mJsonWriter.close();
} finally {
- // the contentprovider only exports data if the writable
- // bit is cleared.
boolean success = mFile.setWritable(false, false);
mLoggingState = LOGGING_STATE_STOPPED;
}
return null;
}
});
+ removeAnyScheduledFlush();
mExecutor.shutdown();
mLoggingState = LOGGING_STATE_STOPPING;
break;
@@ -139,27 +139,26 @@ public class ResearchLog {
public boolean isAlive() {
switch (mLoggingState) {
case LOGGING_STATE_UNSTARTED:
+ case LOGGING_STATE_READY:
case LOGGING_STATE_RUNNING:
return true;
}
return false;
}
- public void waitUntilStopped(int timeoutInMs) throws InterruptedException {
+ public void waitUntilStopped(final int timeoutInMs) throws InterruptedException {
+ removeAnyScheduledFlush();
+ mExecutor.shutdown();
mExecutor.awaitTermination(timeoutInMs, TimeUnit.MILLISECONDS);
}
- private boolean isAbortSuccessful;
- public boolean isAbortSuccessful() {
- return isAbortSuccessful;
- }
-
public synchronized void abort() {
switch (mLoggingState) {
case LOGGING_STATE_UNSTARTED:
mLoggingState = LOGGING_STATE_STOPPED;
isAbortSuccessful = true;
break;
+ case LOGGING_STATE_READY:
case LOGGING_STATE_RUNNING:
mExecutor.submit(new Callable<Object>() {
@Override
@@ -173,6 +172,7 @@ public class ResearchLog {
return null;
}
});
+ removeAnyScheduledFlush();
mExecutor.shutdown();
mLoggingState = LOGGING_STATE_STOPPING;
break;
@@ -181,10 +181,16 @@ public class ResearchLog {
}
}
+ private boolean isAbortSuccessful;
+ public boolean isAbortSuccessful() {
+ return isAbortSuccessful;
+ }
+
/* package */ synchronized void flush() {
switch (mLoggingState) {
case LOGGING_STATE_UNSTARTED:
break;
+ case LOGGING_STATE_READY:
case LOGGING_STATE_RUNNING:
removeAnyScheduledFlush();
mExecutor.submit(mFlushCallable);
@@ -197,7 +203,9 @@ public class ResearchLog {
private Callable<Object> mFlushCallable = new Callable<Object>() {
@Override
public Object call() throws Exception {
- mJsonWriter.flush();
+ if (mLoggingState == LOGGING_STATE_RUNNING) {
+ mJsonWriter.flush();
+ }
return null;
}
};
@@ -220,6 +228,7 @@ public class ResearchLog {
switch (mLoggingState) {
case LOGGING_STATE_UNSTARTED:
break;
+ case LOGGING_STATE_READY:
case LOGGING_STATE_RUNNING:
mExecutor.submit(new Callable<Object>() {
@Override
@@ -239,6 +248,7 @@ public class ResearchLog {
switch (mLoggingState) {
case LOGGING_STATE_UNSTARTED:
break;
+ case LOGGING_STATE_READY:
case LOGGING_STATE_RUNNING:
mExecutor.submit(new Callable<Object>() {
@Override
@@ -260,6 +270,11 @@ public class ResearchLog {
void outputEvent(final String[] keys, final Object[] values) {
// not thread safe.
try {
+ if (mJsonWriter == NULL_JSON_WRITER) {
+ mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile)));
+ mJsonWriter.setLenient(true);
+ mJsonWriter.beginArray();
+ }
mJsonWriter.beginObject();
mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis());
mJsonWriter.name(UPTIME_KEY).value(SystemClock.uptimeMillis());
diff --git a/java/src/com/android/inputmethod/research/ResearchLogUploader.java b/java/src/com/android/inputmethod/research/ResearchLogUploader.java
new file mode 100644
index 000000000..3b1213009
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/ResearchLogUploader.java
@@ -0,0 +1,223 @@
+/*
+ * 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 android.Manifest;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.BatteryManager;
+import android.util.Log;
+
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.R.string;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+public final class ResearchLogUploader {
+ private static final String TAG = ResearchLogUploader.class.getSimpleName();
+ private static final int UPLOAD_INTERVAL_IN_MS = 1000 * 60 * 15; // every 15 min
+ private static final int BUF_SIZE = 1024 * 8;
+
+ private final boolean mCanUpload;
+ private final Context mContext;
+ private final File mFilesDir;
+ private final URL mUrl;
+ private final ScheduledExecutorService mExecutor;
+
+ private Runnable doUploadRunnable = new UploadRunnable(null, false);
+
+ public ResearchLogUploader(final Context context, final File filesDir) {
+ mContext = context;
+ mFilesDir = filesDir;
+ final PackageManager packageManager = context.getPackageManager();
+ final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET,
+ context.getPackageName()) == PackageManager.PERMISSION_GRANTED;
+ if (!hasPermission) {
+ mCanUpload = false;
+ mUrl = null;
+ mExecutor = null;
+ return;
+ }
+ URL tempUrl = null;
+ boolean canUpload = false;
+ ScheduledExecutorService executor = null;
+ try {
+ final String urlString = context.getString(R.string.research_logger_upload_url);
+ if (urlString == null || urlString.equals("")) {
+ return;
+ }
+ tempUrl = new URL(urlString);
+ canUpload = true;
+ executor = Executors.newSingleThreadScheduledExecutor();
+ } catch (MalformedURLException e) {
+ tempUrl = null;
+ e.printStackTrace();
+ return;
+ } finally {
+ mCanUpload = canUpload;
+ mUrl = tempUrl;
+ mExecutor = executor;
+ }
+ }
+
+ public void start() {
+ if (mCanUpload) {
+ Log.d(TAG, "scheduling regular uploading");
+ mExecutor.scheduleWithFixedDelay(doUploadRunnable, UPLOAD_INTERVAL_IN_MS,
+ UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS);
+ } else {
+ Log.d(TAG, "no permission to upload");
+ }
+ }
+
+ public void uploadNow(final Callback callback) {
+ // Perform an immediate upload. Note that this should happen even if there is
+ // another upload happening right now, as it may have missed the latest changes.
+ // TODO: Reschedule regular upload tests starting from now.
+ if (mCanUpload) {
+ mExecutor.submit(new UploadRunnable(callback, true));
+ }
+ }
+
+ public interface Callback {
+ public void onUploadCompleted(final boolean success);
+ }
+
+ private boolean isExternallyPowered() {
+ final Intent intent = mContext.registerReceiver(null, new IntentFilter(
+ Intent.ACTION_BATTERY_CHANGED));
+ final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+ return pluggedState == BatteryManager.BATTERY_PLUGGED_AC
+ || pluggedState == BatteryManager.BATTERY_PLUGGED_USB;
+ }
+
+ private boolean hasWifiConnection() {
+ final ConnectivityManager manager =
+ (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ return wifiInfo.isConnected();
+ }
+
+ class UploadRunnable implements Runnable {
+ private final Callback mCallback;
+ private final boolean mForceUpload;
+
+ public UploadRunnable(final Callback callback, final boolean forceUpload) {
+ mCallback = callback;
+ mForceUpload = forceUpload;
+ }
+
+ @Override
+ public void run() {
+ doUpload();
+ }
+
+ private void doUpload() {
+ if (!mForceUpload && (!isExternallyPowered() || !hasWifiConnection())) {
+ return;
+ }
+ if (mFilesDir == null) {
+ return;
+ }
+ final File[] files = mFilesDir.listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File pathname) {
+ return pathname.getName().startsWith(ResearchLogger.FILENAME_PREFIX)
+ && !pathname.canWrite();
+ }
+ });
+ boolean success = true;
+ if (files.length == 0) {
+ success = false;
+ }
+ for (final File file : files) {
+ if (!uploadFile(file)) {
+ success = false;
+ }
+ }
+ if (mCallback != null) {
+ mCallback.onUploadCompleted(success);
+ }
+ }
+
+ private boolean uploadFile(File file) {
+ Log.d(TAG, "attempting upload of " + file.getAbsolutePath());
+ boolean success = false;
+ final int contentLength = (int) file.length();
+ HttpURLConnection connection = null;
+ InputStream fileIs = null;
+ try {
+ fileIs = new FileInputStream(file);
+ connection = (HttpURLConnection) mUrl.openConnection();
+ connection.setRequestMethod("PUT");
+ connection.setDoOutput(true);
+ connection.setFixedLengthStreamingMode(contentLength);
+ final OutputStream os = connection.getOutputStream();
+ final byte[] buf = new byte[BUF_SIZE];
+ int numBytesRead;
+ while ((numBytesRead = fileIs.read(buf)) != -1) {
+ os.write(buf, 0, numBytesRead);
+ }
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ Log.d(TAG, "upload failed: " + connection.getResponseCode());
+ InputStream netIs = connection.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(netIs));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ Log.d(TAG, "| " + reader.readLine());
+ }
+ reader.close();
+ return success;
+ }
+ file.delete();
+ success = true;
+ Log.d(TAG, "upload successful");
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (fileIs != null) {
+ try {
+ fileIs.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ return success;
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 1abfbad13..68bd98a23 100644
--- a/java/src/com/android/inputmethod/latin/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -14,37 +14,56 @@
* the License.
*/
-package com.android.inputmethod.latin;
+package com.android.inputmethod.research;
import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
import android.app.AlertDialog;
+import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
import android.inputmethodservice.InputMethodService;
import android.os.Build;
+import android.os.IBinder;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
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.EditorInfo;
import android.view.inputmethod.InputConnection;
+import android.widget.Button;
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.Dictionary;
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.RichInputConnection;
import com.android.inputmethod.latin.RichInputConnection.Range;
+import com.android.inputmethod.latin.Suggest;
+import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.define.ProductionFlag;
import java.io.File;
-import java.io.FileFilter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -64,19 +83,23 @@ import java.util.UUID;
public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = ResearchLogger.class.getSimpleName();
private static final boolean OUTPUT_ENTIRE_BUFFER = false; // true may disclose private info
+ public static final boolean DEFAULT_USABILITY_STUDY_MODE = false;
/* package */ static boolean sIsLogging = false;
private static final int OUTPUT_FORMAT_VERSION = 1;
private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
- private static final String FILENAME_PREFIX = "researchLog";
+ private static final String PREF_RESEARCH_HAS_SEEN_SPLASH = "pref_research_has_seen_splash";
+ /* package */ static final String FILENAME_PREFIX = "researchLog";
private static final String FILENAME_SUFFIX = ".txt";
private static final SimpleDateFormat TIMESTAMP_DATEFORMAT =
new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US);
+ private static final boolean IS_SHOWING_INDICATOR = true;
+ private static final boolean IS_SHOWING_INDICATOR_CLEARLY = false;
// constants related to specific log points
private static final String WHITESPACE_SEPARATORS = " \t\n\r";
private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1
private static final String PREF_RESEARCH_LOGGER_UUID_STRING = "pref_research_logger_uuid";
- private static final int ABORT_TIMEOUT_IN_MS = 10 * 1000;
+ private static final int ABORT_TIMEOUT_IN_MS = 10 * 1000; // timeout to notify user
private static final ResearchLogger sInstance = new ResearchLogger();
// to write to a different filename, e.g., for testing, set mFile before calling start()
@@ -88,10 +111,11 @@ 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 final List<LogUnit> mIntentionalResearchLogQueue = new ArrayList<LogUnit>();
+ private List<LogUnit> mIntentionalResearchLogQueue = new ArrayList<LogUnit>();
private boolean mIsPasswordView = false;
private boolean mIsLoggingSuspended = false;
+ private SharedPreferences mPrefs;
// digits entered by the user are replaced with this codepoint.
/* package for test */ static final int DIGIT_REPLACEMENT_CODEPOINT =
@@ -101,6 +125,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
private static final String PREF_LAST_CLEANUP_TIME = "pref_last_cleanup_time";
private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = DateUtils.DAY_IN_MILLIS;
private static final long MAX_LOGFILE_AGE_IN_MS = DateUtils.DAY_IN_MILLIS;
+ protected static final int SUSPEND_DURATION_IN_MINUTES = 1;
// set when LatinIME should ignore an onUpdateSelection() callback that
// arises from operations in this class
private static boolean sLatinIMEExpectingUpdateSelection = false;
@@ -109,7 +134,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
private Suggest mSuggest;
private Dictionary mDictionary;
private KeyboardSwitcher mKeyboardSwitcher;
- private Context mContext;
+ private InputMethodService mInputMethodService;
+
+ private ResearchLogUploader mResearchLogUploader;
private ResearchLogger() {
}
@@ -124,7 +151,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (ims == null) {
Log.w(TAG, "IMS is null; logging is off");
} else {
- mContext = ims;
mFilesDir = ims.getFilesDir();
if (mFilesDir == null || !mFilesDir.exists()) {
Log.w(TAG, "IME storage directory does not exist.");
@@ -132,6 +158,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
if (prefs != null) {
mUUIDString = getUUID(prefs);
+ if (!prefs.contains(PREF_USABILITY_STUDY_MODE)) {
+ Editor e = prefs.edit();
+ e.putBoolean(PREF_USABILITY_STUDY_MODE, DEFAULT_USABILITY_STUDY_MODE);
+ e.apply();
+ }
sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
prefs.registerOnSharedPreferenceChangeListener(this);
@@ -145,7 +176,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
e.apply();
}
}
+ mResearchLogUploader = new ResearchLogUploader(ims, mFilesDir);
+ mResearchLogUploader.start();
mKeyboardSwitcher = keyboardSwitcher;
+ mInputMethodService = ims;
+ mPrefs = prefs;
}
private void cleanupLoggingDir(final File dir, final long time) {
@@ -157,6 +192,73 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
+ public void mainKeyboardView_onAttachedToWindow() {
+ maybeShowSplashScreen();
+ }
+
+ private boolean hasSeenSplash() {
+ return mPrefs.getBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, false);
+ }
+
+ private Dialog mSplashDialog = null;
+
+ private void maybeShowSplashScreen() {
+ if (hasSeenSplash()) {
+ return;
+ }
+ if (mSplashDialog != null && mSplashDialog.isShowing()) {
+ return;
+ }
+ final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
+ if (windowToken == null) {
+ return;
+ }
+ mSplashDialog = new Dialog(mInputMethodService, android.R.style.Theme_Holo_Dialog);
+ mSplashDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ mSplashDialog.setContentView(R.layout.research_splash);
+ mSplashDialog.setCancelable(true);
+ final Window w = mSplashDialog.getWindow();
+ final WindowManager.LayoutParams lp = w.getAttributes();
+ lp.token = windowToken;
+ lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+ w.setAttributes(lp);
+ w.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ mSplashDialog.setOnCancelListener(new OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ mInputMethodService.requestHideSelf(0);
+ }
+ });
+ final Button doNotLogButton = (Button) mSplashDialog.findViewById(
+ R.id.research_do_not_log_button);
+ doNotLogButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onUserLoggingElection(false);
+ mSplashDialog.dismiss();
+ }
+ });
+ final Button doLogButton = (Button) mSplashDialog.findViewById(R.id.research_do_log_button);
+ doLogButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onUserLoggingElection(true);
+ mSplashDialog.dismiss();
+ }
+ });
+ mSplashDialog.show();
+ }
+
+ public void onUserLoggingElection(final boolean enableLogging) {
+ setLoggingAllowed(enableLogging);
+ if (mPrefs == null) {
+ return;
+ }
+ final Editor e = mPrefs.edit();
+ e.putBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, true);
+ e.apply();
+ }
+
private File createLogFile(File filesDir) {
final StringBuilder sb = new StringBuilder();
sb.append(FILENAME_PREFIX).append('-');
@@ -166,8 +268,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
return new File(filesDir, sb.toString());
}
- public void start() {
- if (!sIsLogging) {
+ private void start() {
+ maybeShowSplashScreen();
+ updateSuspendedState();
+ requestIndicatorRedraw();
+ if (!isAllowedToLog()) {
// Log.w(TAG, "not in usability mode; not logging");
return;
}
@@ -175,10 +280,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
Log.w(TAG, "IME storage directory does not exist. Cannot start logging.");
return;
}
- if (mMainResearchLog == null || !mMainResearchLog.isAlive()) {
- mMainResearchLog = new ResearchLog(createLogFile(mFilesDir));
- }
try {
+ if (mMainResearchLog == null || !mMainResearchLog.isAlive()) {
+ mMainResearchLog = new ResearchLog(createLogFile(mFilesDir));
+ }
mMainResearchLog.start();
if (mIntentionalResearchLog == null || !mIntentionalResearchLog.isAlive()) {
mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir));
@@ -189,15 +294,26 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
- public void stop() {
+ /* package */ void stop() {
if (mMainResearchLog != null) {
mMainResearchLog.stop();
}
+ if (mIntentionalResearchLog != null) {
+ mIntentionalResearchLog.stop();
+ }
+ }
+
+ private void setLoggingAllowed(boolean enableLogging) {
+ if (mPrefs == null) {
+ return;
+ }
+ Editor e = mPrefs.edit();
+ e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging);
+ e.apply();
+ sIsLogging = enableLogging;
}
public boolean abort() {
- mIsLoggingSuspended = true;
- requestIndicatorRedraw();
boolean didAbortMainLog = false;
if (mMainResearchLog != null) {
mMainResearchLog.abort();
@@ -209,6 +325,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (mMainResearchLog.isAbortSuccessful()) {
didAbortMainLog = true;
}
+ mMainResearchLog = null;
}
boolean didAbortIntentionalLog = false;
if (mIntentionalResearchLog != null) {
@@ -221,6 +338,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (mIntentionalResearchLog.isAbortSuccessful()) {
didAbortIntentionalLog = true;
}
+ mIntentionalResearchLog = null;
}
return didAbortMainLog && didAbortIntentionalLog;
}
@@ -231,19 +349,31 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
- private void logWholeSessionHistory() throws IOException {
- try {
- LogUnit headerLogUnit = new LogUnit();
- headerLogUnit.addLogAtom(EVENTKEYS_INTENTIONAL_LOG, EVENTKEYS_NULLVALUES, false);
- mIntentionalResearchLog.publishAllEvents(headerLogUnit);
- for (LogUnit logUnit : mIntentionalResearchLogQueue) {
- mIntentionalResearchLog.publishAllEvents(logUnit);
- }
- mIntentionalResearchLog.stop();
- mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir));
- mIntentionalResearchLog.start();
- } finally {
- mIntentionalResearchLogQueue.clear();
+ private void restart() {
+ stop();
+ start();
+ }
+
+ private long mResumeTime = 0L;
+ private void suspendLoggingUntil(long time) {
+ mIsLoggingSuspended = true;
+ mResumeTime = time;
+ requestIndicatorRedraw();
+ }
+
+ private void resumeLogging() {
+ mResumeTime = 0L;
+ updateSuspendedState();
+ requestIndicatorRedraw();
+ if (isAllowedToLog()) {
+ restart();
+ }
+ }
+
+ private void updateSuspendedState() {
+ final long time = System.currentTimeMillis();
+ if (time > mResumeTime) {
+ mIsLoggingSuspended = false;
}
}
@@ -256,15 +386,21 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (sIsLogging == false) {
abort();
}
+ requestIndicatorRedraw();
}
- /* package */ void presentResearchDialog(final LatinIME latinIME) {
+ public void presentResearchDialog(final LatinIME latinIME) {
+ if (mInFeedbackDialog) {
+ Toast.makeText(latinIME, R.string.research_please_exit_feedback_form,
+ Toast.LENGTH_LONG).show();
+ return;
+ }
final CharSequence title = latinIME.getString(R.string.english_ime_research_log);
+ final boolean showEnable = mIsLoggingSuspended || !sIsLogging;
final CharSequence[] items = new CharSequence[] {
- latinIME.getString(R.string.note_timestamp_for_researchlog),
- mIsLoggingSuspended ? latinIME.getString(R.string.enable_session_logging) :
- latinIME.getString(R.string.do_not_log_this_session),
- latinIME.getString(R.string.log_whole_session_history),
+ latinIME.getString(R.string.research_feedback_menu_option),
+ showEnable ? latinIME.getString(R.string.research_enable_session_logging) :
+ latinIME.getString(R.string.research_do_not_log_this_session)
};
final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
@Override
@@ -272,41 +408,30 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
di.dismiss();
switch (position) {
case 0:
- userTimestamp();
- Toast.makeText(latinIME, R.string.notify_recorded_timestamp,
- Toast.LENGTH_LONG).show();
+ presentFeedbackDialog(latinIME);
break;
case 1:
- if (mIsLoggingSuspended) {
- mIsLoggingSuspended = false;
- requestIndicatorRedraw();
- Toast toast = Toast.makeText(latinIME,
- R.string.notify_session_logging_enabled, Toast.LENGTH_LONG);
+ if (showEnable) {
+ if (!sIsLogging) {
+ setLoggingAllowed(true);
+ }
+ resumeLogging();
+ Toast.makeText(latinIME,
+ R.string.research_notify_session_logging_enabled,
+ Toast.LENGTH_LONG).show();
} else {
Toast toast = Toast.makeText(latinIME,
- R.string.notify_session_log_deleting, Toast.LENGTH_LONG);
+ R.string.research_notify_session_log_deleting,
+ Toast.LENGTH_LONG);
toast.show();
boolean isLogDeleted = abort();
+ final long currentTime = System.currentTimeMillis();
+ final long resumeTime = currentTime + 1000 * 60 *
+ SUSPEND_DURATION_IN_MINUTES;
+ suspendLoggingUntil(resumeTime);
toast.cancel();
- if (isLogDeleted) {
- Toast.makeText(latinIME, R.string.notify_session_log_deleted,
- Toast.LENGTH_LONG).show();
- } else {
- Toast.makeText(latinIME,
- R.string.notify_session_log_not_deleted, Toast.LENGTH_LONG)
- .show();
- }
- }
- break;
- case 2:
- try {
- logWholeSessionHistory();
- Toast.makeText(latinIME, R.string.notify_session_history_logged,
+ Toast.makeText(latinIME, R.string.research_notify_logging_suspended,
Toast.LENGTH_LONG).show();
- } catch (IOException e) {
- Toast.makeText(latinIME, R.string.notify_session_history_not_logged,
- Toast.LENGTH_LONG).show();
- e.printStackTrace();
}
break;
}
@@ -319,6 +444,83 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
latinIME.showOptionDialog(builder.create());
}
+ private boolean mInFeedbackDialog = false;
+ public void presentFeedbackDialog(LatinIME latinIME) {
+ mInFeedbackDialog = true;
+ latinIME.launchKeyboardedDialogActivity(FeedbackActivity.class);
+ }
+
+ private ResearchLog mFeedbackLog;
+ private List<LogUnit> mFeedbackQueue;
+ private ResearchLog mSavedMainResearchLog;
+ private ResearchLog mSavedIntentionalResearchLog;
+ private List<LogUnit> mSavedIntentionalResearchLogQueue;
+
+ private void saveLogsForFeedback() {
+ mFeedbackLog = mIntentionalResearchLog;
+ if (mIntentionalResearchLogQueue != null) {
+ mFeedbackQueue = new ArrayList<LogUnit>(mIntentionalResearchLogQueue);
+ } else {
+ mFeedbackQueue = null;
+ }
+ mSavedMainResearchLog = mMainResearchLog;
+ mSavedIntentionalResearchLog = mIntentionalResearchLog;
+ mSavedIntentionalResearchLogQueue = mIntentionalResearchLogQueue;
+
+ mMainResearchLog = null;
+ mIntentionalResearchLog = null;
+ mIntentionalResearchLogQueue = new ArrayList<LogUnit>();
+ }
+
+ private static final int LOG_DRAIN_TIMEOUT_IN_MS = 1000 * 5;
+ public void sendFeedback(final String feedbackContents, final boolean includeHistory) {
+ if (includeHistory && mFeedbackLog != null) {
+ try {
+ LogUnit headerLogUnit = new LogUnit();
+ headerLogUnit.addLogAtom(EVENTKEYS_INTENTIONAL_LOG, EVENTKEYS_NULLVALUES, false);
+ mFeedbackLog.publishAllEvents(headerLogUnit);
+ for (LogUnit logUnit : mFeedbackQueue) {
+ mFeedbackLog.publishAllEvents(logUnit);
+ }
+ userFeedback(mFeedbackLog, feedbackContents);
+ mFeedbackLog.stop();
+ try {
+ mFeedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir));
+ mIntentionalResearchLog.start();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ mIntentionalResearchLogQueue.clear();
+ }
+ mResearchLogUploader.uploadNow(null);
+ } else {
+ // create a separate ResearchLog just for feedback
+ final ResearchLog feedbackLog = new ResearchLog(createLogFile(mFilesDir));
+ try {
+ feedbackLog.start();
+ userFeedback(feedbackLog, feedbackContents);
+ feedbackLog.stop();
+ feedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS);
+ mResearchLogUploader.uploadNow(null);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void onLeavingSendFeedbackDialog() {
+ mInFeedbackDialog = false;
+ mMainResearchLog = mSavedMainResearchLog;
+ mIntentionalResearchLog = mSavedIntentionalResearchLog;
+ mIntentionalResearchLogQueue = mSavedIntentionalResearchLogQueue;
+ }
+
public void initSuggest(Suggest suggest) {
mSuggest = suggest;
}
@@ -328,13 +530,50 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
private boolean isAllowedToLog() {
- return !mIsPasswordView && !mIsLoggingSuspended;
+ return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging;
}
public void requestIndicatorRedraw() {
- // invalidate any existing graphics
- if (mKeyboardSwitcher != null) {
- mKeyboardSwitcher.getKeyboardView().invalidateAllKeys();
+ if (!IS_SHOWING_INDICATOR) {
+ return;
+ }
+ if (mKeyboardSwitcher == null) {
+ return;
+ }
+ final KeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
+ if (keyboardView == null) {
+ return;
+ }
+ keyboardView.invalidateAllKeys();
+ }
+
+
+ public void paintIndicator(KeyboardView view, Paint paint, Canvas canvas, int width,
+ int height) {
+ // TODO: Reimplement using a keyboard background image specific to the ResearchLogger
+ // and remove this method.
+ // The check for MainKeyboardView ensures that a red border is only placed around
+ // the main keyboard, not every keyboard.
+ if (IS_SHOWING_INDICATOR && isAllowedToLog() && view instanceof MainKeyboardView) {
+ final int savedColor = paint.getColor();
+ paint.setColor(Color.RED);
+ final Style savedStyle = paint.getStyle();
+ paint.setStyle(Style.STROKE);
+ final float savedStrokeWidth = paint.getStrokeWidth();
+ if (IS_SHOWING_INDICATOR_CLEARLY) {
+ paint.setStrokeWidth(5);
+ canvas.drawRect(0, 0, width, height, paint);
+ } else {
+ // Put a tiny red dot on the screen so a knowledgeable user can check whether
+ // it is enabled. The dot is actually a zero-width, zero-height rectangle,
+ // placed at the lower-right corner of the canvas, painted with a non-zero border
+ // width.
+ paint.setStrokeWidth(3);
+ canvas.drawRect(width, height, width, height, paint);
+ }
+ paint.setColor(savedColor);
+ paint.setStyle(savedStyle);
+ paint.setStrokeWidth(savedStrokeWidth);
}
}
@@ -467,6 +706,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) {
+ if (!isAllowedToLog()) {
+ return;
+ }
+ if (mMainResearchLog == null) {
+ return;
+ }
if (isPrivacySensitive) {
mMainResearchLog.publishPublicEvents(logUnit);
} else {
@@ -536,6 +781,18 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
+ private static String getUUID(final SharedPreferences prefs) {
+ String uuidString = prefs.getString(PREF_RESEARCH_LOGGER_UUID_STRING, null);
+ if (null == uuidString) {
+ UUID uuid = UUID.randomUUID();
+ uuidString = uuid.toString();
+ Editor editor = prefs.edit();
+ editor.putString(PREF_RESEARCH_LOGGER_UUID_STRING, uuidString);
+ editor.apply();
+ }
+ return uuidString;
+ }
+
private String scrubWord(String word) {
if (mDictionary == null) {
return WORD_REPLACEMENT_STRING;
@@ -546,14 +803,84 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
return WORD_REPLACEMENT_STRING;
}
+ // Special methods related to startup, shutdown, logging itself
+
private static final String[] EVENTKEYS_INTENTIONAL_LOG = {
"IntentionalLog"
};
- private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT = {
- "LatinKeyboardViewProcessMotionEvent", "action", "eventTime", "id", "x", "y", "size",
+
+ private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = {
+ "LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions",
+ "fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion"
+ };
+ public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
+ final SharedPreferences prefs) {
+ final ResearchLogger researchLogger = getInstance();
+ if (researchLogger.mInFeedbackDialog) {
+ researchLogger.saveLogsForFeedback();
+ }
+ researchLogger.start();
+ if (editorInfo != null) {
+ final Context context = researchLogger.mInputMethodService;
+ try {
+ final PackageInfo packageInfo;
+ packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),
+ 0);
+ final Integer versionCode = packageInfo.versionCode;
+ final String versionName = packageInfo.versionName;
+ final Object[] values = {
+ researchLogger.mUUIDString, editorInfo.packageName,
+ Integer.toHexString(editorInfo.inputType),
+ Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId,
+ Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName,
+ OUTPUT_FORMAT_VERSION
+ };
+ researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL, values);
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void latinIME_onFinishInputInternal() {
+ 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"
+ };
+
+ private void userFeedback(ResearchLog researchLog, String feedbackContents) {
+ // this method is special; it directs the feedbackContents to a particular researchLog
+ final LogUnit logUnit = new LogUnit();
+ final Object[] values = {
+ feedbackContents
+ };
+ logUnit.addLogAtom(EVENTKEYS_USER_FEEDBACK, values, false);
+ researchLog.publishAllEvents(logUnit);
+ }
+
+ // Regular logging methods
+
+ private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT = {
+ "MainKeyboardViewProcessMotionEvent", "action", "eventTime", "id", "x", "y", "size",
"pressure"
};
- public static void latinKeyboardView_processMotionEvent(final MotionEvent me, final int action,
+ public static void mainKeyboardView_processMotionEvent(final MotionEvent me, final int action,
final long eventTime, final int index, final int id, final int x, final int y) {
if (me != null) {
final String actionString;
@@ -573,7 +900,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
actionString, eventTime, id, x, y, size, pressure
};
getInstance().enqueuePotentiallyPrivateEvent(
- EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT, values);
+ EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT, values);
}
}
@@ -611,19 +938,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values);
}
- 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_LATINIME_DELETESURROUNDINGTEXT = {
"LatinIMEDeleteSurroundingText", "length"
};
@@ -653,7 +967,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
values);
}
- /* package */ static boolean getAndClearLatinIMEExpectingUpdateSelection() {
+ public static boolean getAndClearLatinIMEExpectingUpdateSelection() {
boolean returnValue = sLatinIMEExpectingUpdateSelection;
sLatinIMEExpectingUpdateSelection = false;
return returnValue;
@@ -702,51 +1016,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
// Play it safe. Remove privacy-sensitive events.
researchLogger.publishLogUnit(researchLogger.mCurrentLogUnit, true);
researchLogger.mCurrentLogUnit = new LogUnit();
+ getInstance().stop();
}
}
- private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = {
- "LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions",
- "fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion"
- };
- public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
- final SharedPreferences prefs) {
- final ResearchLogger researchLogger = getInstance();
- researchLogger.start();
- if (editorInfo != null) {
- final Context context = researchLogger.mContext;
- try {
- final PackageInfo packageInfo;
- packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),
- 0);
- final Integer versionCode = packageInfo.versionCode;
- final String versionName = packageInfo.versionName;
- final Object[] values = {
- researchLogger.mUUIDString, editorInfo.packageName,
- Integer.toHexString(editorInfo.inputType),
- Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId,
- Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName,
- OUTPUT_FORMAT_VERSION
- };
- researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL, values);
- } catch (NameNotFoundException e) {
- e.printStackTrace();
- }
- }
- }
-
- private static String getUUID(final SharedPreferences prefs) {
- String uuidString = prefs.getString(PREF_RESEARCH_LOGGER_UUID_STRING, null);
- if (null == uuidString) {
- UUID uuid = UUID.randomUUID();
- uuidString = uuid.toString();
- Editor editor = prefs.edit();
- editor.putString(PREF_RESEARCH_LOGGER_UUID_STRING, uuidString);
- editor.apply();
- }
- return uuidString;
- }
-
private static final String[] EVENTKEYS_LATINIME_ONUPDATESELECTION = {
"LatinIMEOnUpdateSelection", "lastSelectionStart", "lastSelectionEnd", "oldSelStart",
"oldSelEnd", "newSelStart", "newSelEnd", "composingSpanStart", "composingSpanEnd",
@@ -856,23 +1129,24 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
EVENTKEYS_NULLVALUES);
}
- private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_ONLONGPRESS = {
- "LatinKeyboardViewOnLongPress"
+ private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS = {
+ "MainKeyboardViewOnLongPress"
};
- public static void latinKeyboardView_onLongPress() {
- getInstance().enqueueEvent(EVENTKEYS_LATINKEYBOARDVIEW_ONLONGPRESS, EVENTKEYS_NULLVALUES);
+ public static void mainKeyboardView_onLongPress() {
+ getInstance().enqueueEvent(EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS, EVENTKEYS_NULLVALUES);
}
- private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_SETKEYBOARD = {
- "LatinKeyboardViewSetKeyboard", "elementId", "locale", "orientation", "width",
+ private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_SETKEYBOARD = {
+ "MainKeyboardViewSetKeyboard", "elementId", "locale", "orientation", "width",
"modeName", "action", "navigateNext", "navigatePrevious", "clobberSettingsKey",
"passwordInput", "shortcutKeyEnabled", "hasShortcutKey", "languageSwitchKeyEnabled",
"isMultiLine", "tw", "th", "keys"
};
- public static void latinKeyboardView_setKeyboard(final Keyboard keyboard) {
+ public static void mainKeyboardView_setKeyboard(final Keyboard keyboard) {
if (keyboard != null) {
final KeyboardId kid = keyboard.mId;
final boolean isPasswordView = kid.passwordInput();
+ getInstance().setIsPasswordView(isPasswordView);
final Object[] values = {
KeyboardId.elementIdToName(kid.mElementId),
kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
@@ -892,7 +1166,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
keyboard.mOccupiedHeight,
keyboard.mKeys
};
- getInstance().enqueueEvent(EVENTKEYS_LATINKEYBOARDVIEW_SETKEYBOARD, values);
+ getInstance().enqueueEvent(EVENTKEYS_MAINKEYBOARDVIEW_SETKEYBOARD, values);
getInstance().setIsPasswordView(isPasswordView);
}
}
@@ -984,16 +1258,16 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
- private static final String[] EVENTKEYS_SUGGESTIONSVIEW_SETSUGGESTIONS = {
- "SuggestionsViewSetSuggestions", "suggestedWords"
+ private static final String[] EVENTKEYS_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS = {
+ "SuggestionStripViewSetSuggestions", "suggestedWords"
};
- public static void suggestionsView_setSuggestions(final SuggestedWords suggestedWords) {
+ public static void suggestionStripView_setSuggestions(final SuggestedWords suggestedWords) {
if (suggestedWords != null) {
final Object[] values = {
suggestedWords
};
- getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_SUGGESTIONSVIEW_SETSUGGESTIONS,
- values);
+ getInstance().enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS, values);
}
}