GestureStrokeRecognitionPoints.java revision 8aa9963a895f9dd5bb1bc92ab2e4f461e058f87a
1f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka/*
2f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka * Copyright (C) 2012 The Android Open Source Project
3f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka *
48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License.
68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at
7f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka *
88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
9f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka *
108aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and
148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License.
15f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka */
16f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
17f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokapackage com.android.inputmethod.keyboard.internal;
18f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
1980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaokaimport android.content.res.TypedArray;
2002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaokaimport android.util.Log;
2102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka
22f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokaimport com.android.inputmethod.latin.InputPointers;
2380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaokaimport com.android.inputmethod.latin.R;
247519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaokaimport com.android.inputmethod.latin.ResizableIntArray;
2580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaokaimport com.android.inputmethod.latin.ResourceUtils;
26f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
27f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaokapublic class GestureStroke {
2802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private static final String TAG = GestureStroke.class.getSimpleName();
2902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private static final boolean DEBUG = false;
3058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static final boolean DEBUG_SPEED = false;
3102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka
32b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    // The height of extra area above the keyboard to draw gesture trails.
33b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    // Proportional to the keyboard height.
34b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    public static final float EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO = 0.25f;
35b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka
3657f7de0ba664187e13bcea5adff7f5f65eddd823Tadashi G. Takaoka    public static final int DEFAULT_CAPACITY = 128;
3757f7de0ba664187e13bcea5adff7f5f65eddd823Tadashi G. Takaoka
38f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    private final int mPointerId;
397519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka    private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY);
407519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka    private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
417519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka    private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
4202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka
4380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    private final GestureStrokeParams mParams;
4480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka
4558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mKeyWidth; // pixel
46b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    private int mMinYCoordinate; // pixel
47b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    private int mMaxYCoordinate; // pixel
4858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    // Static threshold for starting gesture detection
4902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private int mDetectFastMoveSpeedThreshold; // pixel /sec
5002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private int mDetectFastMoveTime;
5102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private int mDetectFastMoveX;
5202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private int mDetectFastMoveY;
5358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    // Dynamic threshold for gesture after fast typing
5458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private boolean mAfterFastTyping;
5558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mGestureDynamicDistanceThresholdFrom; // pixel
5658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mGestureDynamicDistanceThresholdTo; // pixel
5758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    // Variables for gesture sampling
5858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mGestureSamplingMinimumDistance; // pixel
5958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private long mLastMajorEventTime;
6058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mLastMajorEventX;
6158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mLastMajorEventY;
6258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    // Variables for gesture recognition
6358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mGestureRecognitionSpeedThreshold; // pixel / sec
6458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mIncrementalRecognitionSize;
6558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int mLastIncrementalBatchSize;
66f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
6780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    public static final class GestureStrokeParams {
6880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Static threshold for gesture after fast typing
6980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final int mStaticTimeThresholdAfterFastTyping; // msec
7080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Static threshold for starting gesture detection
7180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final float mDetectFastMoveSpeedThreshold; // keyWidth/sec
7280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Dynamic threshold for gesture after fast typing
7380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final int mDynamicThresholdDecayDuration; // msec
7480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Time based threshold values
7580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final int mDynamicTimeThresholdFrom; // msec
7680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final int mDynamicTimeThresholdTo; // msec
7780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Distance based threshold values
7880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final float mDynamicDistanceThresholdFrom; // keyWidth
7980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final float mDynamicDistanceThresholdTo; // keyWidth
8080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Parameters for gesture sampling
8180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final float mSamplingMinimumDistance; // keyWidth
8280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Parameters for gesture recognition
8380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final int mRecognitionMinimumTime; // msec
8480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public final float mRecognitionSpeedThreshold; // keyWidth/sec
8558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
8680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        // Default GestureStroke parameters for test.
8780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public static final GestureStrokeParams FOR_TEST = new GestureStrokeParams();
8880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public static final GestureStrokeParams DEFAULT = FOR_TEST;
8958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
9080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        private GestureStrokeParams() {
9180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            // These parameter values are default and intended for testing.
9280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mStaticTimeThresholdAfterFastTyping = 350; // msec
9380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDetectFastMoveSpeedThreshold = 1.5f; // keyWidth / sec
9480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicThresholdDecayDuration = 450; // msec
9580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicTimeThresholdFrom = 300; // msec
9680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicTimeThresholdTo = 20; // msec
9780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicDistanceThresholdFrom = 6.0f; // keyWidth
9880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicDistanceThresholdTo = 0.35f; // keyWidth
9980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            // The following parameters' change will affect the result of regression test.
10080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mSamplingMinimumDistance = 1.0f / 6.0f; // keyWidth
10180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mRecognitionMinimumTime = 100; // msec
10280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mRecognitionSpeedThreshold = 5.5f; // keyWidth / sec
10380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        }
10458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
10580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        public GestureStrokeParams(final TypedArray mainKeyboardViewAttr) {
10680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mStaticTimeThresholdAfterFastTyping = mainKeyboardViewAttr.getInt(
10780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping,
10880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mStaticTimeThresholdAfterFastTyping);
10980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDetectFastMoveSpeedThreshold = ResourceUtils.getFraction(mainKeyboardViewAttr,
11080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureDetectFastMoveSpeedThreshold,
11180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mDetectFastMoveSpeedThreshold);
11280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicThresholdDecayDuration = mainKeyboardViewAttr.getInt(
11380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureDynamicThresholdDecayDuration,
11480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mDynamicThresholdDecayDuration);
11580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicTimeThresholdFrom = mainKeyboardViewAttr.getInt(
11680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureDynamicTimeThresholdFrom,
11780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mDynamicTimeThresholdFrom);
11880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicTimeThresholdTo = mainKeyboardViewAttr.getInt(
11980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureDynamicTimeThresholdTo,
12080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mDynamicTimeThresholdTo);
12180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicDistanceThresholdFrom = ResourceUtils.getFraction(mainKeyboardViewAttr,
12280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureDynamicDistanceThresholdFrom,
12380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mDynamicDistanceThresholdFrom);
12480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mDynamicDistanceThresholdTo = ResourceUtils.getFraction(mainKeyboardViewAttr,
12580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureDynamicDistanceThresholdTo,
12680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mDynamicDistanceThresholdTo);
12780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mSamplingMinimumDistance = ResourceUtils.getFraction(mainKeyboardViewAttr,
12880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureSamplingMinimumDistance,
12980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mSamplingMinimumDistance);
13080bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mRecognitionMinimumTime = mainKeyboardViewAttr.getInt(
13180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureRecognitionMinimumTime,
13280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mRecognitionMinimumTime);
13380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            mRecognitionSpeedThreshold = ResourceUtils.getFraction(mainKeyboardViewAttr,
13480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureRecognitionSpeedThreshold,
13580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    DEFAULT.mRecognitionSpeedThreshold);
13680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        }
13780bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    }
13858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
13958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private static final int MSEC_PER_SEC = 1000;
140f80f09c7eed430827ae8294a5b0f33d5f21cee60Tadashi G. Takaoka
14180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    public GestureStroke(final int pointerId, final GestureStrokeParams params) {
142f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka        mPointerId = pointerId;
14380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mParams = params;
144f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
145f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
146b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
14702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mKeyWidth = keyWidth;
148b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        mMinYCoordinate = -(int)(keyboardHeight * EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
149b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        mMaxYCoordinate = keyboardHeight - 1;
1501e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
15180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mDetectFastMoveSpeedThreshold = (int)(keyWidth * mParams.mDetectFastMoveSpeedThreshold);
15258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        mGestureDynamicDistanceThresholdFrom =
15380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                (int)(keyWidth * mParams.mDynamicDistanceThresholdFrom);
15480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mGestureDynamicDistanceThresholdTo = (int)(keyWidth * mParams.mDynamicDistanceThresholdTo);
15580bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mGestureSamplingMinimumDistance = (int)(keyWidth * mParams.mSamplingMinimumDistance);
15680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        mGestureRecognitionSpeedThreshold = (int)(keyWidth * mParams.mRecognitionSpeedThreshold);
15702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (DEBUG) {
15858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format(
15958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    "[%d] setKeyboardGeometry: keyWidth=%3d tT=%3d >> %3d tD=%3d >> %3d",
16058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    mPointerId, keyWidth,
16180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    mParams.mDynamicTimeThresholdFrom,
16280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                    mParams.mDynamicTimeThresholdTo,
16358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    mGestureDynamicDistanceThresholdFrom,
16458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    mGestureDynamicDistanceThresholdTo));
16502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        }
166f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
167f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
168b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka    public int getLength() {
169b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka        return mEventTimes.getLength();
170b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka    }
171b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka
17258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    public void onDownEvent(final int x, final int y, final long downTime,
17358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            final long gestureFirstDownTime, final long lastTypingTime) {
17458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        reset();
17558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final long elapsedTimeAfterTyping = downTime - lastTypingTime;
17680bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        if (elapsedTimeAfterTyping < mParams.mStaticTimeThresholdAfterFastTyping) {
177630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka            mAfterFastTyping = true;
178630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        }
179630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        if (DEBUG) {
18058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] onDownEvent: dT=%3d%s", mPointerId,
18158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    elapsedTimeAfterTyping, mAfterFastTyping ? " afterFastTyping" : ""));
182630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        }
18358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final int elapsedTimeFromFirstDown = (int)(downTime - gestureFirstDownTime);
184b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        addPointOnKeyboard(x, y, elapsedTimeFromFirstDown, true /* isMajorEvent */);
185630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka    }
186630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka
18758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int getGestureDynamicDistanceThreshold(final int deltaTime) {
18880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        if (!mAfterFastTyping || deltaTime >= mParams.mDynamicThresholdDecayDuration) {
18958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            return mGestureDynamicDistanceThresholdTo;
190630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        }
191630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        final int decayedThreshold =
19258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                (mGestureDynamicDistanceThresholdFrom - mGestureDynamicDistanceThresholdTo)
19380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                * deltaTime / mParams.mDynamicThresholdDecayDuration;
19458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        return mGestureDynamicDistanceThresholdFrom - decayedThreshold;
19558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    }
19658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
19758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    private int getGestureDynamicTimeThreshold(final int deltaTime) {
19880bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        if (!mAfterFastTyping || deltaTime >= mParams.mDynamicThresholdDecayDuration) {
19980bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka            return mParams.mDynamicTimeThresholdTo;
20058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        }
20158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final int decayedThreshold =
20280bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                (mParams.mDynamicTimeThresholdFrom - mParams.mDynamicTimeThresholdTo)
20380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka                * deltaTime / mParams.mDynamicThresholdDecayDuration;
20480bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        return mParams.mDynamicTimeThresholdFrom - decayedThreshold;
205630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka    }
206630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka
2077a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka    public final boolean isStartOfAGesture() {
2087a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        if (!hasDetectedFastMove()) {
20902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            return false;
21002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        }
211b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka        final int size = getLength();
21202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (size <= 0) {
21302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            return false;
21402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        }
21502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int lastIndex = size - 1;
21602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int deltaTime = mEventTimes.get(lastIndex) - mDetectFastMoveTime;
2177a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        if (deltaTime < 0) {
2187a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            return false;
2197a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        }
22058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final int deltaDistance = getDistance(
22102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex),
22202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                mDetectFastMoveX, mDetectFastMoveY);
22358fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final int distanceThreshold = getGestureDynamicDistanceThreshold(deltaTime);
22458fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final int timeThreshold = getGestureDynamicTimeThreshold(deltaTime);
22558fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka        final boolean isStartOfAGesture = deltaTime >= timeThreshold
22658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                && deltaDistance >= distanceThreshold;
22702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (DEBUG) {
22858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            Log.d(TAG, String.format("[%d] isStartOfAGesture: dT=%3d tT=%3d dD=%3d tD=%3d%s%s",
22958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    mPointerId, deltaTime, timeThreshold,
23058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    deltaDistance, distanceThreshold,
23158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    mAfterFastTyping ? " afterFastTyping" : "",
23258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    isStartOfAGesture ? " startOfAGesture" : ""));
23302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        }
23402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        return isStartOfAGesture;
235f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
236f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
23772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    public void duplicateLastPointWith(final int time) {
238b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka        final int lastIndex = getLength() - 1;
23972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        if (lastIndex >= 0) {
24072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final int x = mXCoordinates.get(lastIndex);
24172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            final int y = mYCoordinates.get(lastIndex);
242915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            if (DEBUG) {
243915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka                Log.d(TAG, String.format("[%d] duplicateLastPointWith: %d,%d|%d", mPointerId,
244915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka                        x, y, time));
245915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            }
24672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            // TODO: Have appendMajorPoint()
24772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            appendPoint(x, y, time);
24872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            updateIncrementalRecognitionSize(x, y, time);
24972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        }
25072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka    }
25172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
25258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    protected void reset() {
2531e6f39a9f994e21b749a1cbae55a3adbfb5640e9Tadashi G. Takaoka        mIncrementalRecognitionSize = 0;
2540c5f72e2bf22df48af051827f97ab6052026d531Tom Ouyang        mLastIncrementalBatchSize = 0;
2557519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        mEventTimes.setLength(0);
2567519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        mXCoordinates.setLength(0);
2577519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        mYCoordinates.setLength(0);
25802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mLastMajorEventTime = 0;
25902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mDetectFastMoveTime = 0;
260630d9c95e596c902b80dcb57eb0386e94290406dTadashi G. Takaoka        mAfterFastTyping = false;
26102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    }
26202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka
26302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private void appendPoint(final int x, final int y, final int time) {
264b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka        final int lastIndex = getLength() - 1;
265915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        // The point that is created by {@link duplicateLastPointWith(int)} may have later event
266915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        // time than the next {@link MotionEvent}. To maintain the monotonicity of the event time,
267915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        // drop the successive point here.
268915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        if (lastIndex >= 0 && mEventTimes.get(lastIndex) > time) {
269915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            Log.w(TAG, String.format("[%d] drop stale event: %d,%d|%d last: %d,%d|%d", mPointerId,
270915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka                    x, y, time, mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex),
271915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka                    mEventTimes.get(lastIndex)));
272915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            return;
273915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        }
27402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mEventTimes.add(time);
27502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mXCoordinates.add(x);
27602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mYCoordinates.add(y);
277f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
278f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
27902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private void updateMajorEvent(final int x, final int y, final int time) {
28002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mLastMajorEventTime = time;
28102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mLastMajorEventX = x;
28202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        mLastMajorEventY = y;
28302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    }
28402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka
285c9ba26994b946d35c375cd1cd9a6db2b23b3de7eTadashi G. Takaoka    private final boolean hasDetectedFastMove() {
2867a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka        return mDetectFastMoveTime > 0;
2877a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka    }
2887a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka
28902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private int detectFastMove(final int x, final int y, final int time) {
290b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka        final int size = getLength();
29102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int lastIndex = size - 1;
29202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int lastX = mXCoordinates.get(lastIndex);
29302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int lastY = mYCoordinates.get(lastIndex);
29402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int dist = getDistance(lastX, lastY, x, y);
29502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int msecs = time - mEventTimes.get(lastIndex);
29602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (msecs > 0) {
29702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            final int pixels = getDistance(lastX, lastY, x, y);
29802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            final int pixelsPerSec = pixels * MSEC_PER_SEC;
29958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            if (DEBUG_SPEED) {
30002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
30158fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                Log.d(TAG, String.format("[%d] detectFastMove: speed=%5.2f", mPointerId, speed));
30202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            }
30302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            // Equivalent to (pixels / msecs < mStartSpeedThreshold / MSEC_PER_SEC)
3047a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka            if (!hasDetectedFastMove() && pixelsPerSec > mDetectFastMoveSpeedThreshold * msecs) {
30502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                if (DEBUG) {
30658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
30758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                    Log.d(TAG, String.format(
30858fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                            "[%d] detectFastMove: speed=%5.2f T=%3d points=%3d fastMove",
30958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka                            mPointerId, speed, time, size));
31002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                }
31102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                mDetectFastMoveTime = time;
31202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                mDetectFastMoveX = x;
31302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                mDetectFastMoveY = y;
31402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            }
315f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka        }
31602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        return dist;
31702a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    }
31802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka
319b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    /**
320b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * Add a touch event as a gesture point. Returns true if the touch event is on the valid
321b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * gesture area.
322b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * @param x the x-coordinate of the touch event
323b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * @param y the y-coordinate of the touch event
324b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * @param time the elapsed time in millisecond from the first gesture down
325b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * @param isMajorEvent false if this is a historical move event
326b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     * @return true if the touch event is on the valid gesture area
327b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka     */
328b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka    public boolean addPointOnKeyboard(final int x, final int y, final int time,
329b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka            final boolean isMajorEvent) {
330b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka        final int size = getLength();
33102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (size <= 0) {
33202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            // Down event
33302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            appendPoint(x, y, time);
33402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            updateMajorEvent(x, y, time);
33502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        } else {
33658fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            final int distance = detectFastMove(x, y, time);
33758fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            if (distance > mGestureSamplingMinimumDistance) {
33802a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka                appendPoint(x, y, time);
33902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            }
340f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka        }
34102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (isMajorEvent) {
34261dcaaf17e4d1f9e941b961559a46823e6e25c99Tadashi G. Takaoka            updateIncrementalRecognitionSize(x, y, time);
34302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            updateMajorEvent(x, y, time);
34461dcaaf17e4d1f9e941b961559a46823e6e25c99Tadashi G. Takaoka        }
345b3f789799a2983a9c97288686f11dfab369243c0Tadashi G. Takaoka        return y >= mMinYCoordinate && y < mMaxYCoordinate;
34661dcaaf17e4d1f9e941b961559a46823e6e25c99Tadashi G. Takaoka    }
34761dcaaf17e4d1f9e941b961559a46823e6e25c99Tadashi G. Takaoka
34861dcaaf17e4d1f9e941b961559a46823e6e25c99Tadashi G. Takaoka    private void updateIncrementalRecognitionSize(final int x, final int y, final int time) {
34902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int msecs = (int)(time - mLastMajorEventTime);
35002a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (msecs <= 0) {
35102a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka            return;
35202a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        }
35302a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int pixels = getDistance(mLastMajorEventX, mLastMajorEventY, x, y);
35402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int pixelsPerSec = pixels * MSEC_PER_SEC;
35502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        // Equivalent to (pixels / msecs < mGestureRecognitionThreshold / MSEC_PER_SEC)
35602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        if (pixelsPerSec < mGestureRecognitionSpeedThreshold * msecs) {
357b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka            mIncrementalRecognitionSize = getLength();
358f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka        }
35958fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka    }
36058fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka
36180bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka    public final boolean hasRecognitionTimePast(
36258fe5a421f3334641209300c5bc60c0e6a842220Tadashi G. Takaoka            final long currentTime, final long lastRecognitionTime) {
36380bcb9963259994cfb6497a19709198414aa860aTadashi G. Takaoka        return currentTime > lastRecognitionTime + mParams.mRecognitionMinimumTime;
364f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
365f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
3667a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka    public final void appendAllBatchPoints(final InputPointers out) {
367b2f5d1525093e66faa4a46d6cf10c0144fca2041Tadashi G. Takaoka        appendBatchPoints(out, getLength());
368f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
369f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
3707a17c1fcb52f0249108cfcbd789928320706718aTadashi G. Takaoka    public final void appendIncrementalBatchPoints(final InputPointers out) {
3717519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        appendBatchPoints(out, mIncrementalRecognitionSize);
3727519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka    }
3737519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka
3747519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka    private void appendBatchPoints(final InputPointers out, final int size) {
3756c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        final int length = size - mLastIncrementalBatchSize;
3766c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        if (length <= 0) {
3776c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka            return;
3786c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka        }
3797519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        out.append(mPointerId, mEventTimes, mXCoordinates, mYCoordinates,
3806c3304ea961fd4da0a1da01dc1fac4797c713bccTadashi G. Takaoka                mLastIncrementalBatchSize, length);
3817519091f7c15c50a9a1e50d82fa92400335852ecTadashi G. Takaoka        mLastIncrementalBatchSize = size;
382f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
383f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka
38402a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka    private static int getDistance(final int x1, final int y1, final int x2, final int y2) {
38502a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int dx = x1 - x2;
38602a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        final int dy = y1 - y2;
387bcec82de66f52655593dc233346f11468f5077a0Ken Wakasa        // Note that, in recent versions of Android, FloatMath is actually slower than
388bcec82de66f52655593dc233346f11468f5077a0Ken Wakasa        // java.lang.Math due to the way the JIT optimizes java.lang.Math.
38902a67200fc25d1be9dfbc35e3bb4b59bef28f386Tadashi G. Takaoka        return (int)Math.sqrt(dx * dx + dy * dy);
390f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka    }
391f39fccbd0fd63647c52e8eabcb60df69f97492b5Tadashi G. Takaoka}
392