A complete 3D game development suite written purely in Java.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
jmonkeyengine/jme3-android/src/main/java/com/jme3/input/android/AndroidGestureHandler.java

348 lines
17 KiB

/*
* Copyright (c) 2009-2012 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.input.android;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import com.jme3.input.event.InputEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.input.event.TouchEvent;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* AndroidGestureHandler uses Gesture type listeners to create jME TouchEvents
* for gestures. This class is designed to handle the gestures supported
* on Android rev 9 (Android 2.3). Extend this class to add functionality
* added by Android after rev 9.
*
* @author iwgeric
*/
public class AndroidGestureHandler implements
GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener,
ScaleGestureDetector.OnScaleGestureListener {
private static final Logger logger = Logger.getLogger(AndroidGestureHandler.class.getName());
private AndroidInputHandler androidInput;
private GestureDetector gestureDetector;
private ScaleGestureDetector scaleDetector;
float gestureDownX = -1f;
float gestureDownY = -1f;
float scaleStartX = -1f;
float scaleStartY = -1f;
public AndroidGestureHandler(AndroidInputHandler androidInput) {
this.androidInput = androidInput;
}
public void initialize() {
}
public void destroy() {
setView(null);
}
public void setView(View view) {
if (view != null) {
gestureDetector = new GestureDetector(view.getContext(), this);
scaleDetector = new ScaleGestureDetector(view.getContext(), this);
} else {
gestureDetector = null;
scaleDetector = null;
}
}
public void detectGesture(MotionEvent event) {
if (gestureDetector != null && scaleDetector != null) {
gestureDetector.onTouchEvent(event);
scaleDetector.onTouchEvent(event);
}
}
private int getPointerIndex(MotionEvent event) {
return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
}
private int getPointerId(MotionEvent event) {
return event.getPointerId(getPointerIndex(event));
}
private void processEvent(TouchEvent event) {
// Add the touch event
androidInput.addEvent(event);
if (androidInput.isSimulateMouse()) {
InputEvent mouseEvent = generateMouseEvent(event);
if (mouseEvent != null) {
// Add the mouse event
androidInput.addEvent(mouseEvent);
}
}
}
// TODO: Ring Buffer for mouse events?
private InputEvent generateMouseEvent(TouchEvent event) {
InputEvent inputEvent = null;
int newX;
int newY;
int newDX;
int newDY;
if (androidInput.isMouseEventsInvertX()) {
newX = (int) (androidInput.invertX(event.getX()));
newDX = (int)event.getDeltaX() * -1;
} else {
newX = (int) event.getX();
newDX = (int)event.getDeltaX();
}
int wheel = (int) (event.getScaleSpan()); // might need to scale to match mouse wheel
int dWheel = (int) (event.getDeltaScaleSpan()); // might need to scale to match mouse wheel
if (androidInput.isMouseEventsInvertY()) {
newY = (int) (androidInput.invertY(event.getY()));
newDY = (int)event.getDeltaY() * -1;
} else {
newY = (int) event.getY();
newDY = (int)event.getDeltaY();
}
switch (event.getType()) {
case SCALE_MOVE:
inputEvent = new MouseMotionEvent(newX, newY, newDX, newDY, wheel, dWheel);
inputEvent.setTime(event.getTime());
break;
}
return inputEvent;
}
/* Events from onGestureListener */
public boolean onDown(MotionEvent event) {
// start of all GestureListeners. Not really a gesture by itself
// so we don't create an event.
// However, reset the scaleInProgress here since this is the beginning
// of a series of gesture events.
// logger.log(Level.INFO, "onDown pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
gestureDownX = androidInput.getJmeX(event.getX());
gestureDownY = androidInput.invertY(androidInput.getJmeY(event.getY()));
return true;
}
public boolean onSingleTapUp(MotionEvent event) {
// Up of single tap. May be followed by a double tap later.
// use onSingleTapConfirmed instead.
// logger.log(Level.INFO, "onSingleTapUp pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
return true;
}
public void onShowPress(MotionEvent event) {
// logger.log(Level.INFO, "onShowPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
float jmeX = androidInput.getJmeX(event.getX());
float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SHOWPRESS, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(getPointerId(event));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure());
processEvent(touchEvent);
}
public void onLongPress(MotionEvent event) {
// logger.log(Level.INFO, "onLongPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
float jmeX = androidInput.getJmeX(event.getX());
float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.LONGPRESSED, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(getPointerId(event));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure());
processEvent(touchEvent);
}
public boolean onScroll(MotionEvent startEvent, MotionEvent endEvent, float distX, float distY) {
// if not scaleInProgess, send scroll events. This is to avoid sending
// scroll events when one of the fingers is lifted just before the other one.
// Avoids sending the scroll for that brief period of time.
// Return true so that the next event doesn't accumulate the distX and distY values.
// Apparantly, both distX and distY are negative.
// Negate distX to get the real value, but leave distY negative to compensate
// for the fact that jME has y=0 at bottom where Android has y=0 at top.
// if (!scaleInProgress) {
if (!scaleDetector.isInProgress()) {
// logger.log(Level.INFO, "onScroll pointerId: {0}, startAction: {1}, startX: {2}, startY: {3}, endAction: {4}, endX: {5}, endY: {6}, dx: {7}, dy: {8}",
// new Object[]{getPointerId(startEvent), getAction(startEvent), startEvent.getX(), startEvent.getY(), getAction(endEvent), endEvent.getX(), endEvent.getY(), distX, distY});
float jmeX = androidInput.getJmeX(endEvent.getX());
float jmeY = androidInput.invertY(androidInput.getJmeY(endEvent.getY()));
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SCROLL, jmeX, jmeY, androidInput.getJmeX(-distX), androidInput.getJmeY(distY));
touchEvent.setPointerId(getPointerId(endEvent));
touchEvent.setTime(endEvent.getEventTime());
touchEvent.setPressure(endEvent.getPressure());
processEvent(touchEvent);
}
return true;
}
public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float velocityX, float velocityY) {
// Fling happens only once at the end of the gesture (all fingers up).
// Fling returns the velocity of the finger movement in pixels/sec.
// Therefore, the dX and dY values are actually velocity instead of distance values
// Since this does not track the movement, use the start position and velocity values.
// logger.log(Level.INFO, "onFling pointerId: {0}, startAction: {1}, startX: {2}, startY: {3}, endAction: {4}, endX: {5}, endY: {6}, velocityX: {7}, velocityY: {8}",
// new Object[]{getPointerId(startEvent), getAction(startEvent), startEvent.getX(), startEvent.getY(), getAction(endEvent), endEvent.getX(), endEvent.getY(), velocityX, velocityY});
float jmeX = androidInput.getJmeX(startEvent.getX());
float jmeY = androidInput.invertY(androidInput.getJmeY(startEvent.getY()));
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.FLING, jmeX, jmeY, velocityX, velocityY);
touchEvent.setPointerId(getPointerId(endEvent));
touchEvent.setTime(endEvent.getEventTime());
touchEvent.setPressure(endEvent.getPressure());
processEvent(touchEvent);
return true;
}
/* Events from onDoubleTapListener */
public boolean onSingleTapConfirmed(MotionEvent event) {
// Up of single tap when no double tap followed.
// logger.log(Level.INFO, "onSingleTapConfirmed pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
float jmeX = androidInput.getJmeX(event.getX());
float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.TAP, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(getPointerId(event));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure());
processEvent(touchEvent);
return true;
}
public boolean onDoubleTap(MotionEvent event) {
//The down motion event of the first tap of the double-tap
// We could use this event to fire off a double tap event, or use
// DoubleTapEvent with a check for the UP action
// logger.log(Level.INFO, "onDoubleTap pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
float jmeX = androidInput.getJmeX(event.getX());
float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.DOUBLETAP, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(getPointerId(event));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure());
processEvent(touchEvent);
return true;
}
public boolean onDoubleTapEvent(MotionEvent event) {
//Notified when an event within a double-tap gesture occurs, including the down, move(s), and up events.
// this means it will get called multiple times for a single double tap
// logger.log(Level.INFO, "onDoubleTapEvent pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
// if (getAction(event) == MotionEvent.ACTION_UP) {
// TouchEvent touchEvent = touchEventPool.getNextFreeEvent();
// touchEvent.set(TouchEvent.Type.DOUBLETAP, event.getX(), androidInput.invertY(event.getY()), 0, 0);
// touchEvent.setPointerId(getPointerId(event));
// touchEvent.setTime(event.getEventTime());
// touchEvent.setPressure(event.getPressure());
// processEvent(touchEvent);
// }
return true;
}
/* Events from ScaleGestureDetector */
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
// Scale uses a focusX and focusY instead of x and y. Focus is the middle
// of the fingers. Therefore, use the x and y values from the Down event
// so that the x and y values don't jump to the middle position.
// return true or all gestures for this beginning event will be discarded
logger.log(Level.INFO, "onScaleBegin");
scaleStartX = gestureDownX;
scaleStartY = gestureDownY;
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SCALE_START, scaleStartX, scaleStartY, 0f, 0f);
touchEvent.setPointerId(0);
touchEvent.setTime(scaleGestureDetector.getEventTime());
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
touchEvent.setDeltaScaleSpan(0f);
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress());
processEvent(touchEvent);
return true;
}
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
// return true or all gestures for this event will be accumulated
logger.log(Level.INFO, "onScale");
scaleStartX = gestureDownX;
scaleStartY = gestureDownY;
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SCALE_MOVE, scaleStartX, scaleStartY, 0f, 0f);
touchEvent.setPointerId(0);
touchEvent.setTime(scaleGestureDetector.getEventTime());
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress());
processEvent(touchEvent);
return true;
}
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
logger.log(Level.INFO, "onScaleEnd");
scaleStartX = gestureDownX;
scaleStartY = gestureDownY;
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SCALE_END, scaleStartX, scaleStartY, 0f, 0f);
touchEvent.setPointerId(0);
touchEvent.setTime(scaleGestureDetector.getEventTime());
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress());
processEvent(touchEvent);
}
}