Translates Android Framework MotionEvents into Ink stroke events, performing basic analysis based on the current MotionEvent and the previously received touch input. - Android User Interface

Android examples for User Interface:Touch Event

Description

Translates Android Framework MotionEvents into Ink stroke events, performing basic analysis based on the current MotionEvent and the previously received touch input.

Demo Code

/*/*from  w w w. ja  v a  2  s.c o m*/
 * Created by Zahari Pastarmadjiev.
 * Copyright (c) 2013 Wacom. All rights reserved.
 */
import android.view.MotionEvent;

public class Main{
    private final static Logger logger = new Logger(TouchUtils.class, true);
    private final static float MINIMUM_POINT_DISTANCE = 2f;
    /**
     * Constant:  for {@link #filterMotionEventForInking(MotionEvent, TouchPointID)}: The current motion event couldn't be interpreted as valid stroke event by the Wacom Ink.
     */
    public final static int STROKE_EVENT_FAIL = -1;
    /**
     * Constant for {@link #filterMotionEventForInking(MotionEvent, TouchPointID)}: The current motion event should be interpreted as stroke begin event by the Wacom Ink.
     */
    public final static int STROKE_EVENT_BEGIN = 0;
    /**
     * Constant for {@link #filterMotionEventForInking(MotionEvent, TouchPointID)}: The current motion event should be interpreted as stroke move event by the Wacom Ink.
     */
    public final static int STROKE_EVENT_MOVE = 1;
    /**
     * Constant for {@link #filterMotionEventForInking(MotionEvent, TouchPointID)}: The current motion event should be interpreted as stroke end event by the Wacom Ink.
     */
    public final static int STROKE_EVENT_END = 2;
    /**
     * Constant for {@link #filterMotionEventForInking(MotionEvent, TouchPointID)}: The current motion event should be interpreted as stroke end event by the Wacom Ink. 
     * This constant is used when the Android Framework reports a MotionEvent.ACTION_CANCEL event, which should be treated as stroke end event. 
     * Because the current MotionEvent is outside the screen, in this particular scenario the previous touch event's data should be used for the stroke end event ({@link TouchPointID#getOldX()}, {@link TouchPointID#getOldY()}).
     * 
     */
    public final static int STROKE_EVENT_FORCEEND = 3;
    /**
     * Translates Android Framework MotionEvents into Wacom Ink stroke events, performing basic analysis based on the current MotionEvent and the previously received touch input.
     * <p/> According to the InkingEngine Core's documentation, a stroke's life cycle starts with a stroke begin event, followed by at least one stroke move event and finishes with a stroke end event.
     * <p/>In order to ensure the correct Wacom Ink stroke's life cycle, the Android Framework's touch input is being filtered for every dispatched MotionEvent, 
     * the expected stroke's state is being returned and the current touch event's relevant data (x,y coordinates, pressure, action type, etc.) are being stored in a TouchPointID object in order 
     * to be used again as reference during the next call of this method.
     * @param motionEvent an MotionEvent reported by the Android Framework.
     * @param touchPointID a TouchPointID object containing relevant data of the previous MotionEvent.
     * 
     * @return The expected stroke's event.
     * <p/>Possible values:
     * <br/>{@link #STROKE_EVENT_BEGIN}, 
     * <br/>{@link #STROKE_EVENT_MOVE}, 
     * <br/>{@link #STROKE_EVENT_END}, 
     * <br/>{@link #STROKE_EVENT_FAIL}, 
     * <br/>{@link #STROKE_EVENT_FORCEEND}
     */
    public static int filterMotionEventForInking(MotionEvent motionEvent,
            TouchPointID touchPointID) {
        int activePointerIndex = motionEvent.getActionIndex();
        int currentPointerId = motionEvent.getPointerId(activePointerIndex);

        int failCode = STROKE_EVENT_FAIL;

        switch (motionEvent.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
            if (touchPointID.isValid()) {
                throw new RuntimeException("ACTION_DOWN / already inking?");
            } else {
                touchPointID.setData(motionEvent);
            }
            if (logger.isEnabled()) {
                if (Logger.LOG_ENABLED)
                    logger.d("ACTION_DOWN / OK, down");
            }
            return STROKE_EVENT_BEGIN;

        case MotionEvent.ACTION_POINTER_DOWN:
            if (touchPointID.isInvalid()) {
                //no prev point so we can begin, it's ok
                touchPointID.setData(motionEvent, activePointerIndex);
                if (Logger.LOG_ENABLED)
                    logger.d("ACTION_POINTER_DOWN / OK, ptr_down: no prev point so we can begin");
                return STROKE_EVENT_BEGIN;
            } else {
                //prev point available, it can't be, fail!
                if (Logger.LOG_ENABLED)
                    logger.d("ACTION_POINTER_DOWN / FAIL, ptr_down: prev point available, it can't be");
                return failCode;
            }

        case MotionEvent.ACTION_MOVE:
            if (touchPointID.isInvalid()) {
                if (Logger.LOG_ENABLED)
                    logger.d("ACTION_MOVE / FAIL, move: invalid last point");
                return failCode;
            }
            int prevPointerIndex = motionEvent
                    .findPointerIndex(touchPointID.getPointerId());
            if (prevPointerIndex == -1) {
                //glitch: prev pointer id disappeared?! it's impossible! fail!
                throw new RuntimeException(
                        "ACTION_MOVE / prev pointer id disappeared?! it's impossible! fail!");
            } else if (!hasReallyMoved(motionEvent.getX(prevPointerIndex),
                    motionEvent.getY(prevPointerIndex),
                    touchPointID.getX(), touchPointID.getY())) {
                //glitch: new move event, but the x,y coordinates are the same as the prev ones, fail!
                if (Logger.LOG_ENABLED)
                    logger.d("ACTION_MOVE / FAIL, move: new move event, but the x,y coordinates are the same as the prev ones");
                return failCode;
            } else {
                //use prev. pointer id to take the data, it's ok
                if (Logger.LOG_ENABLED)
                    logger.d("ACTION_MOVE / OK, move: use prev. pointer id to take the data");
                touchPointID.setData(motionEvent, prevPointerIndex);
                return STROKE_EVENT_MOVE;
            }

        case MotionEvent.ACTION_UP:
            if (touchPointID.isInvalid()) {
                //glitch: prev point it missing?! it's impossible! fail!
                if (Logger.LOG_ENABLED)
                    logger.d("ACTION_UP / FAIL, up: prev point it missing?! it's impossible!");
                return failCode;
            } else {
                //prev point it available, it's ok
                if (Logger.LOG_ENABLED)
                    logger.d("ACTION_UP / OK, up: prev point is available");
                touchPointID.invalidate();
                return STROKE_EVENT_END;
            }

        case MotionEvent.ACTION_POINTER_UP:
            if (touchPointID.isInvalid()) {
                //no prev point, discard point, fail!
                if (Logger.LOG_ENABLED)
                    logger.d("ACTION_POINTER_UP / FAIL, ptr_up: no prev point, discard point");
                return failCode;
            }
            if (currentPointerId == touchPointID.getPointerId()) {
                //prev pointer id is the same, it's ok
                touchPointID.invalidate();
                if (Logger.LOG_ENABLED)
                    logger.d("ACTION_POINTER_UP / OK, ptr_up: prev pointer id is the same");
                return STROKE_EVENT_END;
            } else {
                //prev pointer id is different, fail!
                return failCode;
            }

        case MotionEvent.ACTION_CANCEL:
            if (touchPointID.isValid()) {
                touchPointID.invalidate();
                if (Logger.LOG_ENABLED)
                    logger.d("OK, cancel: treat point as up, use prev. x,y");
                return STROKE_EVENT_FORCEEND;
                //Debug: debugHistory.add("13. ACTION_CANCEL lastWritePoint!=null; copy the last point and treat the point as PEN_UP; " + motionEvent);
            } else {
                if (Logger.LOG_ENABLED)
                    logger.d("FAIL, cancel: cancel");
                return failCode;
            }
        }
        if (Logger.LOG_ENABLED)
            logger.d("FAIL, unknown: ?");
        return failCode;
    }
    private static boolean hasReallyMoved(float x, float y, float oldX,
            float oldY) {
        double dist = Math.sqrt((oldX - x) * (oldX - x) + (oldY - y)
                * (oldY - y));
        if (dist < MINIMUM_POINT_DISTANCE) {
            if (Logger.LOG_ENABLED)
                logger.d("not 'moved': " + dist);
            return false;
        } else {
            return true;
        }
    }
}

Related Tutorials