Android examples for User Interface:Touch Event
Translates Android Framework MotionEvents into Ink stroke events, performing basic analysis based on the current MotionEvent and the previously received touch input.
/*/*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; } } }