GestureStrokeRecognitionPoints.java revision 915f348b35cb66ed9696a51c9250f9b25799fb82
1f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka/*
2f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka * Copyright (C) 2012 The Android Open Source Project
3f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka *
4f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka * in compliance with the License. You may obtain a copy of the License at
6f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka *
7f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0
8f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka *
9f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software distributed under the License
10f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka * or implied. See the License for the specific language governing permissions and limitations under
12f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka * the License.
13f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka */
14f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
15f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokapackage com.android.inputmethod.keyboard.internal;
16f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
1780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaokaimport android.content.res.TypedArray;
1802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaokaimport android.util.Log;
1902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka
20f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokaimport com.android.inputmethod.latin.InputPointers;
2180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaokaimport com.android.inputmethod.latin.R;
227519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaokaimport com.android.inputmethod.latin.ResizableIntArray;
2380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaokaimport com.android.inputmethod.latin.ResourceUtils;
24f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
25f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokapublic class GestureStroke {
2602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private static final String TAG = GestureStroke.class.getSimpleName();
2702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private static final boolean DEBUG = false;
2858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static final boolean DEBUG_SPEED = false;
2902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka
30b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    // The height of extra area above the keyboard to draw gesture trails.
31b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    // Proportional to the keyboard height.
32b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    public static final float EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO = 0.25f;
33b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka
3457f7de0ba664187e13bcea5adff7f5f65eddd823Tadashi G. Takaoka    public static final int DEFAULT_CAPACITY = 128;
3557f7de0ba664187e13bcea5adff7f5f65eddd823Tadashi G. Takaoka
36f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    private final int mPointerId;
377519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka    private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY);
387519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka    private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
397519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka    private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
4002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka
4180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    private final GestureStrokeParams mParams;
4280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka
4358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mKeyWidth; // pixel
44b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    private int mMinYCoordinate; // pixel
45b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    private int mMaxYCoordinate; // pixel
4658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    // Static threshold for starting gesture detection
4702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private int mDetectFastMoveSpeedThreshold; // pixel /sec
4802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private int mDetectFastMoveTime;
4902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private int mDetectFastMoveX;
5002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private int mDetectFastMoveY;
5158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    // Dynamic threshold for gesture after fast typing
5258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private boolean mAfterFastTyping;
5358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mGestureDynamicDistanceThresholdFrom; // pixel
5458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mGestureDynamicDistanceThresholdTo; // pixel
5558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    // Variables for gesture sampling
5658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mGestureSamplingMinimumDistance; // pixel
5758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private long mLastMajorEventTime;
5858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mLastMajorEventX;
5958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mLastMajorEventY;
6058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    // Variables for gesture recognition
6158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mGestureRecognitionSpeedThreshold; // pixel / sec
6258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mIncrementalRecognitionSize;
6358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mLastIncrementalBatchSize;
64f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
6580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    public static final class GestureStrokeParams {
6680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Static threshold for gesture after fast typing
6780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final int mStaticTimeThresholdAfterFastTyping; // msec
6880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Static threshold for starting gesture detection
6980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final float mDetectFastMoveSpeedThreshold; // keyWidth/sec
7080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Dynamic threshold for gesture after fast typing
7180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final int mDynamicThresholdDecayDuration; // msec
7280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Time based threshold values
7380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final int mDynamicTimeThresholdFrom; // msec
7480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final int mDynamicTimeThresholdTo; // msec
7580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Distance based threshold values
7680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final float mDynamicDistanceThresholdFrom; // keyWidth
7780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final float mDynamicDistanceThresholdTo; // keyWidth
7880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Parameters for gesture sampling
7980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final float mSamplingMinimumDistance; // keyWidth
8080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Parameters for gesture recognition
8180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final int mRecognitionMinimumTime; // msec
8280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final float mRecognitionSpeedThreshold; // keyWidth/sec
8358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
8480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Default GestureStroke parameters for test.
8580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public static final GestureStrokeParams FOR_TEST = new GestureStrokeParams();
8680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public static final GestureStrokeParams DEFAULT = FOR_TEST;
8758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
8880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        private GestureStrokeParams() {
8980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            // These parameter values are default and intended for testing.
9080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mStaticTimeThresholdAfterFastTyping = 350; // msec
9180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDetectFastMoveSpeedThreshold = 1.5f; // keyWidth / sec
9280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicThresholdDecayDuration = 450; // msec
9380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicTimeThresholdFrom = 300; // msec
9480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicTimeThresholdTo = 20; // msec
9580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicDistanceThresholdFrom = 6.0f; // keyWidth
9680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicDistanceThresholdTo = 0.35f; // keyWidth
9780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            // The following parameters' change will affect the result of regression test.
9880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mSamplingMinimumDistance = 1.0f / 6.0f; // keyWidth
9980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mRecognitionMinimumTime = 100; // msec
10080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mRecognitionSpeedThreshold = 5.5f; // keyWidth / sec
10180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        }
10258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
10380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public GestureStrokeParams(final TypedArray mainKeyboardViewAttr) {
10480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mStaticTimeThresholdAfterFastTyping = mainKeyboardViewAttr.getInt(
10580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping,
10680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mStaticTimeThresholdAfterFastTyping);
10780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDetectFastMoveSpeedThreshold = ResourceUtils.getFraction(mainKeyboardViewAttr,
10880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureDetectFastMoveSpeedThreshold,
10980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mDetectFastMoveSpeedThreshold);
11080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicThresholdDecayDuration = mainKeyboardViewAttr.getInt(
11180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureDynamicThresholdDecayDuration,
11280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mDynamicThresholdDecayDuration);
11380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicTimeThresholdFrom = mainKeyboardViewAttr.getInt(
11480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureDynamicTimeThresholdFrom,
11580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mDynamicTimeThresholdFrom);
11680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicTimeThresholdTo = mainKeyboardViewAttr.getInt(
11780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureDynamicTimeThresholdTo,
11880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mDynamicTimeThresholdTo);
11980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicDistanceThresholdFrom = ResourceUtils.getFraction(mainKeyboardViewAttr,
12080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureDynamicDistanceThresholdFrom,
12180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mDynamicDistanceThresholdFrom);
12280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicDistanceThresholdTo = ResourceUtils.getFraction(mainKeyboardViewAttr,
12380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureDynamicDistanceThresholdTo,
12480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mDynamicDistanceThresholdTo);
12580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mSamplingMinimumDistance = ResourceUtils.getFraction(mainKeyboardViewAttr,
12680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureSamplingMinimumDistance,
12780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mSamplingMinimumDistance);
12880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mRecognitionMinimumTime = mainKeyboardViewAttr.getInt(
12980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureRecognitionMinimumTime,
13080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mRecognitionMinimumTime);
13180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mRecognitionSpeedThreshold = ResourceUtils.getFraction(mainKeyboardViewAttr,
13280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureRecognitionSpeedThreshold,
13380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mRecognitionSpeedThreshold);
13480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        }
13580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    }
13658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
13758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static final int MSEC_PER_SEC = 1000;
138f80f09c7eed430827ae8294a5b0f33d5f21cee60Tadashi G. Takaoka
13980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    public GestureStroke(final int pointerId, final GestureStrokeParams params) {
140f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka        mPointerId = pointerId;
14180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mParams = params;
142f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
143f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
144b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
14502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mKeyWidth = keyWidth;
146b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        mMinYCoordinate = -(int)(keyboardHeight * EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
147b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        mMaxYCoordinate = keyboardHeight - 1;
1481e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
14980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mDetectFastMoveSpeedThreshold = (int)(keyWidth * mParams.mDetectFastMoveSpeedThreshold);
15058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        mGestureDynamicDistanceThresholdFrom =
15180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                (int)(keyWidth * mParams.mDynamicDistanceThresholdFrom);
15280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mGestureDynamicDistanceThresholdTo = (int)(keyWidth * mParams.mDynamicDistanceThresholdTo);
15380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mGestureSamplingMinimumDistance = (int)(keyWidth * mParams.mSamplingMinimumDistance);
15480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mGestureRecognitionSpeedThreshold = (int)(keyWidth * mParams.mRecognitionSpeedThreshold);
15502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (DEBUG) {
15658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format(
15758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    "[%d] setKeyboardGeometry: keyWidth=%3d tT=%3d >> %3d tD=%3d >> %3d",
15858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    mPointerId, keyWidth,
15980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    mParams.mDynamicTimeThresholdFrom,
16080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    mParams.mDynamicTimeThresholdTo,
16158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    mGestureDynamicDistanceThresholdFrom,
16258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    mGestureDynamicDistanceThresholdTo));
16302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        }
164f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
165f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
16658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    public void onDownEvent(final int x, final int y, final long downTime,
16758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            final long gestureFirstDownTime, final long lastTypingTime) {
16858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        reset();
16958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final long elapsedTimeAfterTyping = downTime - lastTypingTime;
17080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        if (elapsedTimeAfterTyping < mParams.mStaticTimeThresholdAfterFastTyping) {
171630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka            mAfterFastTyping = true;
172630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        }
173630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        if (DEBUG) {
17458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onDownEvent: dT=%3d%s", mPointerId,
17558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    elapsedTimeAfterTyping, mAfterFastTyping ? " afterFastTyping" : ""));
176630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        }
17758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final int elapsedTimeFromFirstDown = (int)(downTime - gestureFirstDownTime);
178b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        addPointOnKeyboard(x, y, elapsedTimeFromFirstDown, true /* isMajorEvent */);
179630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka    }
180630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka
18158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int getGestureDynamicDistanceThreshold(final int deltaTime) {
18280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        if (!mAfterFastTyping || deltaTime >= mParams.mDynamicThresholdDecayDuration) {
18358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            return mGestureDynamicDistanceThresholdTo;
184630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        }
185630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        final int decayedThreshold =
18658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                (mGestureDynamicDistanceThresholdFrom - mGestureDynamicDistanceThresholdTo)
18780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                * deltaTime / mParams.mDynamicThresholdDecayDuration;
18858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        return mGestureDynamicDistanceThresholdFrom - decayedThreshold;
18958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    }
19058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
19158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int getGestureDynamicTimeThreshold(final int deltaTime) {
19280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        if (!mAfterFastTyping || deltaTime >= mParams.mDynamicThresholdDecayDuration) {
19380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            return mParams.mDynamicTimeThresholdTo;
19458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
19558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final int decayedThreshold =
19680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                (mParams.mDynamicTimeThresholdFrom - mParams.mDynamicTimeThresholdTo)
19780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                * deltaTime / mParams.mDynamicThresholdDecayDuration;
19880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        return mParams.mDynamicTimeThresholdFrom - decayedThreshold;
199630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka    }
200630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka
2017a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka    public final boolean isStartOfAGesture() {
2027a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        if (!hasDetectedFastMove()) {
20302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            return false;
20402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        }
20574d0bb09c700aec91afd120688c56498d93e3110Tadashi G. Takaoka        final int size = mEventTimes.getLength();
20602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (size <= 0) {
20702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            return false;
20802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        }
20902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int lastIndex = size - 1;
21002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int deltaTime = mEventTimes.get(lastIndex) - mDetectFastMoveTime;
2117a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        if (deltaTime < 0) {
2127a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return false;
2137a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        }
21458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final int deltaDistance = getDistance(
21502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex),
21602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                mDetectFastMoveX, mDetectFastMoveY);
21758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final int distanceThreshold = getGestureDynamicDistanceThreshold(deltaTime);
21858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final int timeThreshold = getGestureDynamicTimeThreshold(deltaTime);
21958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final boolean isStartOfAGesture = deltaTime >= timeThreshold
22058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                && deltaDistance >= distanceThreshold;
22102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (DEBUG) {
22258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] isStartOfAGesture: dT=%3d tT=%3d dD=%3d tD=%3d%s%s",
22358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    mPointerId, deltaTime, timeThreshold,
22458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    deltaDistance, distanceThreshold,
22558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    mAfterFastTyping ? " afterFastTyping" : "",
22658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    isStartOfAGesture ? " startOfAGesture" : ""));
22702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        }
22802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        return isStartOfAGesture;
229f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
230f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
23172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    public void duplicateLastPointWith(final int time) {
23272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        final int lastIndex = mEventTimes.getLength() - 1;
23372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        if (lastIndex >= 0) {
23472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final int x = mXCoordinates.get(lastIndex);
23572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final int y = mYCoordinates.get(lastIndex);
236915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            if (DEBUG) {
237915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka                Log.d(TAG, String.format("[%d] duplicateLastPointWith: %d,%d|%d", mPointerId,
238915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka                        x, y, time));
239915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            }
24072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            // TODO: Have appendMajorPoint()
24172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            appendPoint(x, y, time);
24272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            updateIncrementalRecognitionSize(x, y, time);
24372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        }
24472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
24572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
24658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    protected void reset() {
2471e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        mIncrementalRecognitionSize = 0;
2480c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang        mLastIncrementalBatchSize = 0;
2497519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        mEventTimes.setLength(0);
2507519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        mXCoordinates.setLength(0);
2517519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        mYCoordinates.setLength(0);
25202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mLastMajorEventTime = 0;
25302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mDetectFastMoveTime = 0;
254630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        mAfterFastTyping = false;
25502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    }
25602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka
25702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private void appendPoint(final int x, final int y, final int time) {
258915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        final int lastIndex = mEventTimes.getLength() - 1;
259915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        // The point that is created by {@link duplicateLastPointWith(int)} may have later event
260915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        // time than the next {@link MotionEvent}. To maintain the monotonicity of the event time,
261915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        // drop the successive point here.
262915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        if (lastIndex >= 0 && mEventTimes.get(lastIndex) > time) {
263915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            Log.w(TAG, String.format("[%d] drop stale event: %d,%d|%d last: %d,%d|%d", mPointerId,
264915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka                    x, y, time, mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex),
265915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka                    mEventTimes.get(lastIndex)));
266915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            return;
267915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        }
26802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mEventTimes.add(time);
26902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mXCoordinates.add(x);
27002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mYCoordinates.add(y);
271f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
272f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
27302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private void updateMajorEvent(final int x, final int y, final int time) {
27402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mLastMajorEventTime = time;
27502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mLastMajorEventX = x;
27602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mLastMajorEventY = y;
27702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    }
27802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka
279c9ba26994b946d35c375cd1cd9a6db2b23b3de7eTadashi G. Takaoka    private final boolean hasDetectedFastMove() {
2807a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        return mDetectFastMoveTime > 0;
2817a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka    }
2827a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka
28302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private int detectFastMove(final int x, final int y, final int time) {
2847519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        final int size = mEventTimes.getLength();
28502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int lastIndex = size - 1;
28602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int lastX = mXCoordinates.get(lastIndex);
28702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int lastY = mYCoordinates.get(lastIndex);
28802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int dist = getDistance(lastX, lastY, x, y);
28902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int msecs = time - mEventTimes.get(lastIndex);
29002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (msecs > 0) {
29102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            final int pixels = getDistance(lastX, lastY, x, y);
29202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            final int pixelsPerSec = pixels * MSEC_PER_SEC;
29358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            if (DEBUG_SPEED) {
29402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
29558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                Log.d(TAG, String.format("[%d] detectFastMove: speed=%5.2f", mPointerId, speed));
29602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            }
29702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            // Equivalent to (pixels / msecs < mStartSpeedThreshold / MSEC_PER_SEC)
2987a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            if (!hasDetectedFastMove() && pixelsPerSec > mDetectFastMoveSpeedThreshold * msecs) {
29902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                if (DEBUG) {
30058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
30158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    Log.d(TAG, String.format(
30258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                            "[%d] detectFastMove: speed=%5.2f T=%3d points=%3d fastMove",
30358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                            mPointerId, speed, time, size));
30402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                }
30502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                mDetectFastMoveTime = time;
30602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                mDetectFastMoveX = x;
30702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                mDetectFastMoveY = y;
30802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            }
309f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka        }
31002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        return dist;
31102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    }
31202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka
313b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    /**
314b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * Add a touch event as a gesture point. Returns true if the touch event is on the valid
315b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * gesture area.
316b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * @param x the x-coordinate of the touch event
317b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * @param y the y-coordinate of the touch event
318b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * @param time the elapsed time in millisecond from the first gesture down
319b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * @param isMajorEvent false if this is a historical move event
320b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * @return true if the touch event is on the valid gesture area
321b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     */
322b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    public boolean addPointOnKeyboard(final int x, final int y, final int time,
323b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            final boolean isMajorEvent) {
32402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int size = mEventTimes.getLength();
32502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (size <= 0) {
32602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            // Down event
32702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            appendPoint(x, y, time);
32802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            updateMajorEvent(x, y, time);
32902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        } else {
33058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            final int distance = detectFastMove(x, y, time);
33158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            if (distance > mGestureSamplingMinimumDistance) {
33202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                appendPoint(x, y, time);
33302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            }
334f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka        }
33502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (isMajorEvent) {
33661dcaaf17e4d1f9e941b961559a46823e6e25c99Tadashi G. Takaoka            updateIncrementalRecognitionSize(x, y, time);
33702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            updateMajorEvent(x, y, time);
33861dcaaf17e4d1f9e941b961559a46823e6e25c99Tadashi G. Takaoka        }
339b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        return y >= mMinYCoordinate && y < mMaxYCoordinate;
34061dcaaf17e4d1f9e941b961559a46823e6e25c99Tadashi G. Takaoka    }
34161dcaaf17e4d1f9e941b961559a46823e6e25c99Tadashi G. Takaoka
34261dcaaf17e4d1f9e941b961559a46823e6e25c99Tadashi G. Takaoka    private void updateIncrementalRecognitionSize(final int x, final int y, final int time) {
34302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int msecs = (int)(time - mLastMajorEventTime);
34402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (msecs <= 0) {
34502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            return;
34602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        }
34702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int pixels = getDistance(mLastMajorEventX, mLastMajorEventY, x, y);
34802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int pixelsPerSec = pixels * MSEC_PER_SEC;
34902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        // Equivalent to (pixels / msecs < mGestureRecognitionThreshold / MSEC_PER_SEC)
35002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (pixelsPerSec < mGestureRecognitionSpeedThreshold * msecs) {
35102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            mIncrementalRecognitionSize = mEventTimes.getLength();
352f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka        }
35358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    }
35458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
35580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    public final boolean hasRecognitionTimePast(
35658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            final long currentTime, final long lastRecognitionTime) {
35780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        return currentTime > lastRecognitionTime + mParams.mRecognitionMinimumTime;
358f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
359f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
3607a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka    public final void appendAllBatchPoints(final InputPointers out) {
3617519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        appendBatchPoints(out, mEventTimes.getLength());
362f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
363f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
3647a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka    public final void appendIncrementalBatchPoints(final InputPointers out) {
3657519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        appendBatchPoints(out, mIncrementalRecognitionSize);
3667519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka    }
3677519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka
3687519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka    private void appendBatchPoints(final InputPointers out, final int size) {
3696c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final int length = size - mLastIncrementalBatchSize;
3706c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (length <= 0) {
3716c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            return;
3726c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        }
3737519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        out.append(mPointerId, mEventTimes, mXCoordinates, mYCoordinates,
3746c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mLastIncrementalBatchSize, length);
3757519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        mLastIncrementalBatchSize = size;
376f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
377f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
37802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private static int getDistance(final int x1, final int y1, final int x2, final int y2) {
37902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int dx = x1 - x2;
38002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int dy = y1 - y2;
381bcec82de66f52655593dc233346f11468f5077a0Ken Wakasa        // Note that, in recent versions of Android, FloatMath is actually slower than
382bcec82de66f52655593dc233346f11468f5077a0Ken Wakasa        // java.lang.Math due to the way the JIT optimizes java.lang.Math.
38302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        return (int)Math.sqrt(dx * dx + dy * dy);
384f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
385f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka}
386