Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6052a81e authored by Tadashi G. Takaoka's avatar Tadashi G. Takaoka Committed by Android (Google) Code Review
Browse files

Merge "Fix gesture detection algorithm" into jb-mr1-dev

parents c8f25294 58fe5a42
Loading
Loading
Loading
Loading
+36 −50
Original line number Diff line number Diff line
@@ -172,8 +172,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
    private static long sLastLetterTypingUpTime;
    private static final InputPointers sAggregratedPointers = new InputPointers(
            GestureStroke.DEFAULT_CAPACITY);
    private static int sLastRecognitionPointSize = 0;
    private static long sLastRecognitionTime = 0;
    private static int sLastRecognitionPointSize = 0; // synchronized using sAggregratedPointers
    private static long sLastRecognitionTime = 0; // synchronized using sAggregratedPointers

    // The position and time at which first down event occurred.
    private long mDownTime;
@@ -310,9 +310,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
        }
        final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
        if (DEBUG_LISTENER) {
            Log.d(TAG, "onPress    : " + KeyDetector.printableCode(key)
                    + " ignoreModifier=" + ignoreModifierKey
                    + " enabled=" + key.isEnabled());
            Log.d(TAG, String.format("[%d] onPress    : %s%s%s", mPointerId,
                    KeyDetector.printableCode(key),
                    ignoreModifierKey ? " ignoreModifier" : "",
                    key.isEnabled() ? "" : " disabled"));
        }
        if (ignoreModifierKey) {
            return false;
@@ -335,10 +336,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
        final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
        final int code = altersCode ? key.getAltCode() : primaryCode;
        if (DEBUG_LISTENER) {
            Log.d(TAG, "onCodeInput: " + Keyboard.printableCode(code)
                    + " text=" + key.getOutputText() + " x=" + x + " y=" + y
                    + " ignoreModifier=" + ignoreModifierKey + " altersCode=" + altersCode
                    + " enabled=" + key.isEnabled());
            final String output = code == Keyboard.CODE_OUTPUT_TEXT
                    ? key.getOutputText() : Keyboard.printableCode(code);
            Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s", mPointerId, x, y,
                    output, ignoreModifierKey ? " ignoreModifier" : "",
                    altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled"));
        }
        if (ProductionFlag.IS_EXPERIMENTAL) {
            ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey,
@@ -367,9 +369,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
        }
        final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
        if (DEBUG_LISTENER) {
            Log.d(TAG, "onRelease  : " + Keyboard.printableCode(primaryCode)
                    + " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey
                    + " enabled="+ key.isEnabled());
            Log.d(TAG, String.format("[%d] onRelease  : %s%s%s%s", mPointerId,
                    Keyboard.printableCode(primaryCode),
                    withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "",
                    key.isEnabled() ?  "": " disabled"));
        }
        if (ProductionFlag.IS_EXPERIMENTAL) {
            ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding,
@@ -385,7 +388,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {

    private void callListenerOnCancelInput() {
        if (DEBUG_LISTENER) {
            Log.d(TAG, "onCancelInput");
            Log.d(TAG, String.format("[%d] onCancelInput", mPointerId));
        }
        if (ProductionFlag.IS_EXPERIMENTAL) {
            ResearchLogger.pointerTracker_callListenerOnCancelInput();
@@ -394,6 +397,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
    }

    private void setKeyDetectorInner(final KeyDetector keyDetector) {
        final Keyboard keyboard = keyDetector.getKeyboard();
        if (keyDetector == mKeyDetector && keyboard == mKeyboard) {
            return;
        }
        mKeyDetector = keyDetector;
        mKeyboard = keyDetector.getKeyboard();
        mGestureStrokeWithPreviewPoints.setKeyboardGeometry(mKeyboard.mMostCommonKeyWidth);
@@ -564,10 +571,15 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
            return;
        }
        if (DEBUG_LISTENER) {
            Log.d(TAG, "onStartBatchInput");
            Log.d(TAG, String.format("[%d] onStartBatchInput", mPointerId));
        }
        sInGesture = true;
        synchronized (sAggregratedPointers) {
            sAggregratedPointers.reset();
            sLastRecognitionPointSize = 0;
            sLastRecognitionTime = 0;
            mListener.onStartBatchInput();
        }
        final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
        mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
    }
@@ -582,7 +594,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
                    sLastRecognitionPointSize = size;
                    sLastRecognitionTime = eventTime;
                    if (DEBUG_LISTENER) {
                        Log.d(TAG, "onUpdateBatchInput: batchPoints=" + size);
                        Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d",
                                mPointerId, size));
                    }
                    mListener.onUpdateBatchInput(sAggregratedPointers);
                }
@@ -595,37 +608,20 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
    private void mayEndBatchInput(final long eventTime) {
        synchronized (sAggregratedPointers) {
            mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
            mGestureStrokeWithPreviewPoints.reset();
            if (getActivePointerTrackerCount() == 1) {
                if (DEBUG_LISTENER) {
                    Log.d(TAG, "onEndBatchInput: batchPoints="
                            + sAggregratedPointers.getPointerSize());
                    Log.d(TAG, String.format("[%d] onEndBatchInput   : batchPoints=%d",
                            mPointerId, sAggregratedPointers.getPointerSize()));
                }
                sInGesture = false;
                sLastBatchInputTime = eventTime;
                mListener.onEndBatchInput(sAggregratedPointers);
                clearBatchInputPointsOfAllPointerTrackers();
            }
        }
        final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
        mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
    }

    private static void abortBatchInput() {
        clearBatchInputPointsOfAllPointerTrackers();
    }

    private static void clearBatchInputPointsOfAllPointerTrackers() {
        final int trackersSize = sTrackers.size();
        for (int i = 0; i < trackersSize; ++i) {
            final PointerTracker tracker = sTrackers.get(i);
            tracker.mGestureStrokeWithPreviewPoints.reset();
        }
        sAggregratedPointers.reset();
        sLastRecognitionPointSize = 0;
        sLastRecognitionTime = 0;
    }

    public void processMotionEvent(final int action, final int x, final int y, final long eventTime,
            final KeyEventHandler handler) {
        switch (action) {
@@ -695,18 +691,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
            if (getActivePointerTrackerCount() == 1) {
                sGestureFirstDownTime = eventTime;
            }
            onGestureDownEvent(x, y, eventTime);
            mGestureStrokeWithPreviewPoints.onDownEvent(x, y, eventTime, sGestureFirstDownTime,
                    sLastLetterTypingUpTime);
        }
    }

    private void onGestureDownEvent(final int x, final int y, final long eventTime) {
        mIsDetectingGesture = true;
        mGestureStrokeWithPreviewPoints.setLastLetterTypingTime(eventTime, sLastLetterTypingUpTime);
        final int elapsedTimeFromFirstDown = (int)(eventTime - sGestureFirstDownTime);
        mGestureStrokeWithPreviewPoints.addPoint(x, y, elapsedTimeFromFirstDown,
                true /* isMajorEvent */);
    }

    private void onDownEventInternal(final int x, final int y, final long eventTime) {
        Key key = onDownKey(x, y, eventTime);
        // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
@@ -939,9 +928,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
            mayEndBatchInput(eventTime);
            return;
        }
        // This event will be recognized as a regular code input. Clear unused possible batch points
        // so they are not mistakenly displayed as preview.
        clearBatchInputPointsOfAllPointerTrackers();

        if (mKeyAlreadyProcessed) {
            return;
        }
@@ -955,7 +942,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
    }

    public void onShowMoreKeysPanel(final int x, final int y, final KeyEventHandler handler) {
        abortBatchInput();
        onLongPressed();
        mIsShowingMoreKeysPanel = true;
        onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
@@ -1043,7 +1029,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
            final long eventTime) {
        final Key key = mKeyDetector.detectHitKey(x, y);
        final String code = KeyDetector.printableCode(key);
        Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %s", title,
                (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, eventTime, code));
        Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
                (mKeyAlreadyProcessed ? "-" : " "), title, x, y, eventTime, code));
    }
}
+102 −64
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import com.android.inputmethod.latin.ResizableIntArray;
public class GestureStroke {
    private static final String TAG = GestureStroke.class.getSimpleName();
    private static final boolean DEBUG = false;
    private static final boolean DEBUG_SPEED = false;

    public static final int DEFAULT_CAPACITY = 128;

@@ -29,42 +30,52 @@ public class GestureStroke {
    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 int mIncrementalRecognitionSize;
    private int mLastIncrementalBatchSize;
    private long mLastMajorEventTime;
    private int mLastMajorEventX;
    private int mLastMajorEventY;
    private boolean mAfterFastTyping;

    private int mKeyWidth;
    private int mStartGestureLengthThresholdAfterFastTyping; // pixel
    private int mStartGestureLengthThreshold; // pixel
    private int mMinGestureSamplingLength; // pixel
    private int mGestureRecognitionSpeedThreshold; // pixel / sec
    private int mKeyWidth; // pixel
    // Static threshold for starting gesture detection
    private int mDetectFastMoveSpeedThreshold; // pixel /sec
    private int mDetectFastMoveTime;
    private int mDetectFastMoveX;
    private int mDetectFastMoveY;
    // Dynamic threshold for gesture after fast typing
    private boolean mAfterFastTyping;
    private int mGestureDynamicDistanceThresholdFrom; // pixel
    private int mGestureDynamicDistanceThresholdTo; // pixel
    // Variables for gesture sampling
    private int mGestureSamplingMinimumDistance; // pixel
    private long mLastMajorEventTime;
    private int mLastMajorEventX;
    private int mLastMajorEventY;
    // Variables for gesture recognition
    private int mGestureRecognitionSpeedThreshold; // pixel / sec
    private int mIncrementalRecognitionSize;
    private int mLastIncrementalBatchSize;

    // TODO: Move some of these to resource.
    private static final int GESTURE_AFTER_FAST_TYPING_DURATION_THRESHOLD = 350; // msec
    private static final float START_GESTURE_LENGTH_THRESHOLD_AFTER_FAST_TYPING_RATIO_TO_KEY_WIDTH =
            8.0f;
    private static final int START_GESTURE_LENGTH_THRESHOLD_DECAY_DURATION = 400; // msec
    private static final float START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH = 0.6f;
    private static final int START_GESTURE_DURATION_THRESHOLD = 70; // msec
    private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec
    private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH = 1.0f / 6.0f;
    private static final float GESTURE_RECOGNITION_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH =
            5.5f; // keyWidth / sec
    private static final float DETECT_FAST_MOVE_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH =
            5.0f; // keyWidth / sec
    private static final int MSEC_PER_SEC = 1000;

    public static final boolean hasRecognitionTimePast(
            final long currentTime, final long lastRecognitionTime) {
        return currentTime > lastRecognitionTime + MIN_GESTURE_RECOGNITION_TIME;
    }
    // Static threshold for gesture after fast typing
    public static final int GESTURE_STATIC_TIME_THRESHOLD_AFTER_FAST_TYPING = 350; // msec

    // Static threshold for starting gesture detection
    private static final float DETECT_FAST_MOVE_SPEED_THRESHOLD = 1.5f; // keyWidth / sec

    // Dynamic threshold for gesture after fast typing
    private static final int GESTURE_DYNAMIC_THRESHOLD_DECAY_DURATION = 450; // msec
    // Time based threshold values
    private static final int GESTURE_DYNAMIC_TIME_THRESHOLD_FROM = 300; // msec
    private static final int GESTURE_DYNAMIC_TIME_THRESHOLD_TO = 20; // msec
    // Distance based threshold values
    private static final float GESTURE_DYNAMIC_DISTANCE_THRESHOLD_FROM = 6.0f; // keyWidth
    private static final float GESTURE_DYNAMIC_DISTANCE_THRESHOLD_TO = 0.35f; // keyWidth

    // Parameters for gesture sampling
    private static final float GESTURE_SAMPLING_MINIMUM_DISTANCE = 1.0f / 6.0f; // keyWidth

    // Parameters for gesture recognition
    private static final int GESTURE_RECOGNITION_MINIMUM_TIME = 100; // msec
    private static final float GESTURE_RECOGNITION_SPEED_THRESHOLD = 5.5f; // keyWidth / sec

    private static final int MSEC_PER_SEC = 1000;

    public GestureStroke(final int pointerId) {
        mPointerId = pointerId;
@@ -73,41 +84,58 @@ public class GestureStroke {
    public void setKeyboardGeometry(final int keyWidth) {
        mKeyWidth = keyWidth;
        // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
        mStartGestureLengthThresholdAfterFastTyping = (int)(keyWidth
                * START_GESTURE_LENGTH_THRESHOLD_AFTER_FAST_TYPING_RATIO_TO_KEY_WIDTH);
        mStartGestureLengthThreshold =
                (int)(keyWidth * START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH);
        mMinGestureSamplingLength = (int)(keyWidth * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH);
        mDetectFastMoveSpeedThreshold = (int)(keyWidth * DETECT_FAST_MOVE_SPEED_THRESHOLD);
        mGestureDynamicDistanceThresholdFrom =
                (int)(keyWidth * GESTURE_DYNAMIC_DISTANCE_THRESHOLD_FROM);
        mGestureDynamicDistanceThresholdTo =
                (int)(keyWidth * GESTURE_DYNAMIC_DISTANCE_THRESHOLD_TO);
        mGestureSamplingMinimumDistance = (int)(keyWidth * GESTURE_SAMPLING_MINIMUM_DISTANCE);
        mGestureRecognitionSpeedThreshold =
                (int)(keyWidth * GESTURE_RECOGNITION_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH);
        mDetectFastMoveSpeedThreshold =
                (int)(keyWidth * DETECT_FAST_MOVE_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH);
                (int)(keyWidth * GESTURE_RECOGNITION_SPEED_THRESHOLD);
        if (DEBUG) {
            Log.d(TAG, "[" + mPointerId + "] setKeyboardGeometry: keyWidth=" + keyWidth
                    + " tL0=" + mStartGestureLengthThresholdAfterFastTyping
                    + " tL=" + mStartGestureLengthThreshold);
            Log.d(TAG, String.format(
                    "[%d] setKeyboardGeometry: keyWidth=%3d tT=%3d >> %3d tD=%3d >> %3d",
                    mPointerId, keyWidth,
                    GESTURE_DYNAMIC_TIME_THRESHOLD_FROM,
                    GESTURE_DYNAMIC_TIME_THRESHOLD_TO,
                    mGestureDynamicDistanceThresholdFrom,
                    mGestureDynamicDistanceThresholdTo));
        }
    }

    public void setLastLetterTypingTime(final long downTime, final long lastTypingTime) {
        final long elpasedTimeAfterTyping = downTime - lastTypingTime;
        if (elpasedTimeAfterTyping < GESTURE_AFTER_FAST_TYPING_DURATION_THRESHOLD) {
    public void onDownEvent(final int x, final int y, final long downTime,
            final long gestureFirstDownTime, final long lastTypingTime) {
        reset();
        final long elapsedTimeAfterTyping = downTime - lastTypingTime;
        if (elapsedTimeAfterTyping < GESTURE_STATIC_TIME_THRESHOLD_AFTER_FAST_TYPING) {
            mAfterFastTyping = true;
        }
        if (DEBUG) {
            Log.d(TAG, "[" + mPointerId + "] setLastTypingTime: dT=" + elpasedTimeAfterTyping
                    + " afterFastTyping=" + mAfterFastTyping);
            Log.d(TAG, String.format("[%d] onDownEvent: dT=%3d%s", mPointerId,
                    elapsedTimeAfterTyping, mAfterFastTyping ? " afterFastTyping" : ""));
        }
        final int elapsedTimeFromFirstDown = (int)(downTime - gestureFirstDownTime);
        addPoint(x, y, elapsedTimeFromFirstDown, true /* isMajorEvent */);
    }

    private int getStartGestureLengthThreshold(final int deltaTime) {
        if (!mAfterFastTyping || deltaTime >= START_GESTURE_LENGTH_THRESHOLD_DECAY_DURATION) {
            return mStartGestureLengthThreshold;
    private int getGestureDynamicDistanceThreshold(final int deltaTime) {
        if (!mAfterFastTyping || deltaTime >= GESTURE_DYNAMIC_THRESHOLD_DECAY_DURATION) {
            return mGestureDynamicDistanceThresholdTo;
        }
        final int decayedThreshold =
                (mStartGestureLengthThresholdAfterFastTyping - mStartGestureLengthThreshold)
                * deltaTime / START_GESTURE_LENGTH_THRESHOLD_DECAY_DURATION;
        return mStartGestureLengthThresholdAfterFastTyping - decayedThreshold;
                (mGestureDynamicDistanceThresholdFrom - mGestureDynamicDistanceThresholdTo)
                * deltaTime / GESTURE_DYNAMIC_THRESHOLD_DECAY_DURATION;
        return mGestureDynamicDistanceThresholdFrom - decayedThreshold;
    }

    private int getGestureDynamicTimeThreshold(final int deltaTime) {
        if (!mAfterFastTyping || deltaTime >= GESTURE_DYNAMIC_THRESHOLD_DECAY_DURATION) {
            return GESTURE_DYNAMIC_TIME_THRESHOLD_TO;
        }
        final int decayedThreshold =
                (GESTURE_DYNAMIC_TIME_THRESHOLD_FROM - GESTURE_DYNAMIC_TIME_THRESHOLD_TO)
                * deltaTime / GESTURE_DYNAMIC_THRESHOLD_DECAY_DURATION;
        return GESTURE_DYNAMIC_TIME_THRESHOLD_FROM - decayedThreshold;
    }

    public boolean isStartOfAGesture() {
@@ -120,21 +148,24 @@ public class GestureStroke {
        }
        final int lastIndex = size - 1;
        final int deltaTime = mEventTimes.get(lastIndex) - mDetectFastMoveTime;
        final int deltaLength = getDistance(
        final int deltaDistance = getDistance(
                mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex),
                mDetectFastMoveX, mDetectFastMoveY);
        final int startGestureLengthThreshold = getStartGestureLengthThreshold(deltaTime);
        final boolean isStartOfAGesture = deltaTime > START_GESTURE_DURATION_THRESHOLD
                && deltaLength > startGestureLengthThreshold;
        final int distanceThreshold = getGestureDynamicDistanceThreshold(deltaTime);
        final int timeThreshold = getGestureDynamicTimeThreshold(deltaTime);
        final boolean isStartOfAGesture = deltaTime >= timeThreshold
                && deltaDistance >= distanceThreshold;
        if (DEBUG) {
            Log.d(TAG, "[" + mPointerId + "] isStartOfAGesture: dT=" + deltaTime
                    + " dL=" + deltaLength + " tL=" + startGestureLengthThreshold
                    + " points=" + size + (isStartOfAGesture ? " Detect start of a gesture" : ""));
            Log.d(TAG, String.format("[%d] isStartOfAGesture: dT=%3d tT=%3d dD=%3d tD=%3d%s%s",
                    mPointerId, deltaTime, timeThreshold,
                    deltaDistance, distanceThreshold,
                    mAfterFastTyping ? " afterFastTyping" : "",
                    isStartOfAGesture ? " startOfAGesture" : ""));
        }
        return isStartOfAGesture;
    }

    public void reset() {
    protected void reset() {
        mIncrementalRecognitionSize = 0;
        mLastIncrementalBatchSize = 0;
        mEventTimes.setLength(0);
@@ -167,15 +198,17 @@ public class GestureStroke {
        if (msecs > 0) {
            final int pixels = getDistance(lastX, lastY, x, y);
            final int pixelsPerSec = pixels * MSEC_PER_SEC;
            if (DEBUG) {
            if (DEBUG_SPEED) {
                final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
                Log.d(TAG, String.format("[" + mPointerId + "] speed=%.3f", speed));
                Log.d(TAG, String.format("[%d] detectFastMove: speed=%5.2f", mPointerId, speed));
            }
            // Equivalent to (pixels / msecs < mStartSpeedThreshold / MSEC_PER_SEC)
            if (mDetectFastMoveTime == 0 && pixelsPerSec > mDetectFastMoveSpeedThreshold * msecs) {
                if (DEBUG) {
                    Log.d(TAG, "[" + mPointerId + "] detect fast move: T="
                            + time + " points = " + size);
                    final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
                    Log.d(TAG, String.format(
                            "[%d] detectFastMove: speed=%5.2f T=%3d points=%3d fastMove",
                            mPointerId, speed, time, size));
                }
                mDetectFastMoveTime = time;
                mDetectFastMoveX = x;
@@ -192,8 +225,8 @@ public class GestureStroke {
            appendPoint(x, y, time);
            updateMajorEvent(x, y, time);
        } else {
            final int dist = detectFastMove(x, y, time);
            if (dist > mMinGestureSamplingLength) {
            final int distance = detectFastMove(x, y, time);
            if (distance > mGestureSamplingMinimumDistance) {
                appendPoint(x, y, time);
            }
        }
@@ -216,6 +249,11 @@ public class GestureStroke {
        }
    }

    public static final boolean hasRecognitionTimePast(
            final long currentTime, final long lastRecognitionTime) {
        return currentTime > lastRecognitionTime + GESTURE_RECOGNITION_MINIMUM_TIME;
    }

    public void appendAllBatchPoints(final InputPointers out) {
        appendBatchPoints(out, mEventTimes.getLength());
    }
+1 −1
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke {
    }

    @Override
    public void reset() {
    protected void reset() {
        super.reset();
        mStrokeId++;
        mLastPreviewSize = 0;
+33 −14

File changed.

Preview size limit exceeded, changes collapsed.