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

Commit dfbc5080 authored by Tadashi G. Takaoka's avatar Tadashi G. Takaoka
Browse files

Refactor SwipeTracker class

This change moves SwipeTracker out from LatinKeyboardBaseView.  Also
this introduces EventBuffer class for swipe tracking buffer.

Bug: 2910379

Change-Id: I48ff714226a248ca063cbaf9755cf45e458f7402
parent 542f057e
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -1286,7 +1286,6 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener
        }

        // Track the last few movements to look for spurious swipes.
        if (action == MotionEvent.ACTION_DOWN) mSwipeTracker.clear();
        mSwipeTracker.addMovement(me);

        // Ignore all motion events until a DOWN.
+91 −66
Original line number Diff line number Diff line
@@ -16,70 +16,40 @@

package com.android.inputmethod.latin;

import android.util.Log;
import android.view.MotionEvent;

class SwipeTracker {
    private static final int NUM_PAST = 4;
    private static final int LONGEST_PAST_TIME = 200;

    static final int NUM_PAST = 4;
    static final int LONGEST_PAST_TIME = 200;
    final EventRingBuffer mBuffer = new EventRingBuffer(NUM_PAST);

    final float mPastX[] = new float[NUM_PAST];
    final float mPastY[] = new float[NUM_PAST];
    final long mPastTime[] = new long[NUM_PAST];

    float mYVelocity;
    float mXVelocity;

    public void clear() {
        mPastTime[0] = 0;
    }
    private float mYVelocity;
    private float mXVelocity;

    public void addMovement(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            clear();
            mBuffer.clear();
            return;
        }
        long time = ev.getEventTime();
        final int N = ev.getHistorySize();
        for (int i=0; i<N; i++) {
            addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i),
                    ev.getHistoricalEventTime(i));
        final int count = ev.getHistorySize();
        for (int i = 0; i < count; i++) {
            addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i), ev.getHistoricalEventTime(i));
        }
        addPoint(ev.getX(), ev.getY(), time);
    }

    private void addPoint(float x, float y, long time) {
        int drop = -1;
        int i;
        final long[] pastTime = mPastTime;
        for (i=0; i<NUM_PAST; i++) {
            if (pastTime[i] == 0) {
        final EventRingBuffer buffer = mBuffer;
        while (buffer.size() > 0) {
            long lastT = buffer.getTime(0);
            if (lastT >= time - LONGEST_PAST_TIME)
                break;
            } else if (pastTime[i] < time-LONGEST_PAST_TIME) {
                drop = i;
            }
        }
        if (i == NUM_PAST && drop < 0) {
            drop = 0;
        }
        if (drop == i) drop--;
        final float[] pastX = mPastX;
        final float[] pastY = mPastY;
        if (drop >= 0) {
            final int start = drop+1;
            final int count = NUM_PAST-drop-1;
            System.arraycopy(pastX, start, pastX, 0, count);
            System.arraycopy(pastY, start, pastY, 0, count);
            System.arraycopy(pastTime, start, pastTime, 0, count);
            i -= (drop+1);
        }
        pastX[i] = x;
        pastY[i] = y;
        pastTime[i] = time;
        i++;
        if (i < NUM_PAST) {
            pastTime[i] = 0;
            buffer.dropOldest();
        }
        buffer.add(x, y, time);
    }

    public void computeCurrentVelocity(int units) {
@@ -87,32 +57,23 @@ class SwipeTracker {
    }

    public void computeCurrentVelocity(int units, float maxVelocity) {
        final float[] pastX = mPastX;
        final float[] pastY = mPastY;
        final long[] pastTime = mPastTime;
        final EventRingBuffer buffer = mBuffer;
        final float oldestX = buffer.getX(0);
        final float oldestY = buffer.getY(0);
        final long oldestTime = buffer.getTime(0);

        final float oldestX = pastX[0];
        final float oldestY = pastY[0];
        final long oldestTime = pastTime[0];
        float accumX = 0;
        float accumY = 0;
        int N=0;
        while (N < NUM_PAST) {
            if (pastTime[N] == 0) {
                break;
            }
            N++;
        }

        for (int i=1; i < N; i++) {
            final int dur = (int)(pastTime[i] - oldestTime);
        final int count = buffer.size();
        for (int pos = 1; pos < count; pos++) {
            final int dur = (int)(buffer.getTime(pos) - oldestTime);
            if (dur == 0) continue;
            float dist = pastX[i] - oldestX;
            float dist = buffer.getX(pos) - oldestX;
            float vel = (dist / dur) * units;   // pixels/frame.
            if (accumX == 0) accumX = vel;
            else accumX = (accumX + vel) * .5f;

            dist = pastY[i] - oldestY;
            dist = buffer.getY(pos) - oldestY;
            vel = (dist / dur) * units;   // pixels/frame.
            if (accumY == 0) accumY = vel;
            else accumY = (accumY + vel) * .5f;
@@ -130,4 +91,68 @@ class SwipeTracker {
    public float getYVelocity() {
        return mYVelocity;
    }

    static class EventRingBuffer {
        private final int bufSize;
        private final float xBuf[];
        private final float yBuf[];
        private final long timeBuf[];
        private int top;  // points new event
        private int end;  // points oldest event
        private int count; // the number of valid data

        public EventRingBuffer(int max) {
            this.bufSize = max;
            xBuf = new float[max];
            yBuf = new float[max];
            timeBuf = new long[max];
            clear();
        }

        public void clear() {
            top = end = count = 0;
        }

        public int size() {
            return count;
        }

        // Position 0 points oldest event
        private int index(int pos) {
            return (end + pos) % bufSize;
        }

        private int advance(int index) {
            return (index + 1) % bufSize;
        }

        public void add(float x, float y, long time) {
            xBuf[top] = x;
            yBuf[top] = y;
            timeBuf[top] = time;
            top = advance(top);
            if (count < bufSize) {
                count++;
            } else {
                end = advance(end);
            }
        }

        public float getX(int pos) {
            return xBuf[index(pos)];
        }

        public float getY(int pos) {
            return yBuf[index(pos)];
        }

        public long getTime(int pos) {
            return timeBuf[index(pos)];
        }

        public void dropOldest() {
            count--;
            end = advance(end);
        }
    }
}
 No newline at end of file
+154 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.android.inputmethod.latin;

import com.android.inputmethod.latin.SwipeTracker.EventRingBuffer;

import android.test.AndroidTestCase;

public class EventRingBufferTests extends AndroidTestCase {

    @Override
    protected void setUp() throws Exception {
        super.setUp();
    }

    @Override
    protected void tearDown() throws Exception {
        super.tearDown();
    }

    private static float X_BASE = 1000f;

    private static float Y_BASE = 2000f;

    private static long TIME_BASE = 3000l;

    private static float x(int id) {
        return X_BASE + id;
    }

    private static float y(int id) {
        return Y_BASE + id;
    }

    private static long time(int id) {
        return TIME_BASE + id;
    }

    private static void addEvent(EventRingBuffer buf, int id) {
        buf.add(x(id), y(id), time(id));
    }

    private static void assertEventSize(EventRingBuffer buf, int size) {
        assertEquals(size, buf.size());
    }

    private static void assertEvent(EventRingBuffer buf, int pos, int id) {
        assertEquals(x(id), buf.getX(pos), 0f);
        assertEquals(y(id), buf.getY(pos), 0f);
        assertEquals(time(id), buf.getTime(pos));
    }

    public void testClearBuffer() {
        EventRingBuffer buf = new EventRingBuffer(4);
        assertEventSize(buf, 0);

        addEvent(buf, 0);
        addEvent(buf, 1);
        addEvent(buf, 2);
        addEvent(buf, 3);
        addEvent(buf, 4);
        assertEventSize(buf, 4);

        buf.clear();
        assertEventSize(buf, 0);
    }

    public void testRingBuffer() {
        EventRingBuffer buf = new EventRingBuffer(4);
        assertEventSize(buf, 0); // [0]

        addEvent(buf, 0);
        assertEventSize(buf, 1); // [1] 0
        assertEvent(buf, 0, 0);

        addEvent(buf, 1);
        addEvent(buf, 2);
        assertEventSize(buf, 3); // [3] 2 1 0
        assertEvent(buf, 0, 0);
        assertEvent(buf, 1, 1);
        assertEvent(buf, 2, 2);

        addEvent(buf, 3);
        assertEventSize(buf, 4); // [4] 3 2 1 0
        assertEvent(buf, 0, 0);
        assertEvent(buf, 1, 1);
        assertEvent(buf, 2, 2);
        assertEvent(buf, 3, 3);

        addEvent(buf, 4);
        addEvent(buf, 5);
        assertEventSize(buf, 4); // [4] 5 4|3 2(1 0)
        assertEvent(buf, 0, 2);
        assertEvent(buf, 1, 3);
        assertEvent(buf, 2, 4);
        assertEvent(buf, 3, 5);

        addEvent(buf, 6);
        addEvent(buf, 7);
        addEvent(buf, 8);
        assertEventSize(buf, 4); // [4] 8 7 6 5|(4 3 2)1|0
        assertEvent(buf, 0, 5);
        assertEvent(buf, 1, 6);
        assertEvent(buf, 2, 7);
        assertEvent(buf, 3, 8);
    }

    public void testDropOldest() {
        EventRingBuffer buf = new EventRingBuffer(4);

        addEvent(buf, 0);
        assertEventSize(buf, 1); // [1] 0
        assertEvent(buf, 0, 0);

        buf.dropOldest();
        assertEventSize(buf, 0); // [0] (0)

        addEvent(buf, 1);
        addEvent(buf, 2);
        addEvent(buf, 3);
        addEvent(buf, 4);
        assertEventSize(buf, 4); // [4] 4|3 2 1(0)
        assertEvent(buf, 0, 1);

        buf.dropOldest();
        assertEventSize(buf, 3); // [3] 4|3 2(1)0
        assertEvent(buf, 0, 2);

        buf.dropOldest();
        assertEventSize(buf, 2); // [2] 4|3(2)10
        assertEvent(buf, 0, 3);

        buf.dropOldest();
        assertEventSize(buf, 1); // [1] 4|(3)210
        assertEvent(buf, 0, 4);

        buf.dropOldest();
        assertEventSize(buf, 0); // [0] (4)|3210
    }
}