Rework of Android input system to support future expansion and gamepad support.

Gamepad support is still a work in progress, but functions.
experimental
iwgeric 10 years ago
parent 60ed6b7620
commit 61ba11d872
  1. 274
      jme3-android/src/main/java/com/jme3/input/android/AndroidGestureProcessor.java
  2. 686
      jme3-android/src/main/java/com/jme3/input/android/AndroidInput.java
  3. 319
      jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java
  4. 158
      jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java
  5. 62
      jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput.java
  6. 108
      jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput14.java
  7. 403
      jme3-android/src/main/java/com/jme3/input/android/AndroidJoystickJoyInput14.java
  8. 140
      jme3-android/src/main/java/com/jme3/input/android/AndroidKeyHandler.java
  9. 17
      jme3-android/src/main/java/com/jme3/input/android/AndroidKeyMapping.java
  10. 48
      jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java
  11. 257
      jme3-android/src/main/java/com/jme3/input/android/AndroidTouchHandler.java
  12. 475
      jme3-android/src/main/java/com/jme3/input/android/AndroidTouchInput.java
  13. 76
      jme3-android/src/main/java/com/jme3/input/android/AndroidTouchInput14.java
  14. 23
      jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java

@ -35,314 +35,240 @@ 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
* 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,
public class AndroidGestureProcessor 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;
private static final Logger logger = Logger.getLogger(AndroidGestureProcessor.class.getName());
private AndroidTouchInput touchInput;
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));
public AndroidGestureProcessor(AndroidTouchInput touchInput) {
this.touchInput = touchInput;
}
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 */
@Override
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()));
// logger.log(Level.INFO, "onDown pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
gestureDownX = touchInput.getJmeX(event.getX());
gestureDownY = touchInput.invertY(touchInput.getJmeY(event.getY()));
return true;
}
@Override
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()});
// logger.log(Level.INFO, "onSingleTapUp pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
return true;
}
@Override
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();
// logger.log(Level.INFO, "onShowPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
float jmeX = touchInput.getJmeX(event.getX());
float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SHOWPRESS, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(getPointerId(event));
touchEvent.setPointerId(touchInput.getPointerId(event));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure());
processEvent(touchEvent);
touchInput.addEvent(touchEvent);
}
@Override
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();
// logger.log(Level.INFO, "onLongPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
float jmeX = touchInput.getJmeX(event.getX());
float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.LONGPRESSED, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(getPointerId(event));
touchEvent.setPointerId(touchInput.getPointerId(event));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure());
processEvent(touchEvent);
touchInput.addEvent(touchEvent);
}
@Override
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.
// 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});
if (!touchInput.getScaleDetector().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[]{touchInput.getPointerId(startEvent), touchInput.getAction(startEvent), startEvent.getX(), startEvent.getY(), touchInput.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));
float jmeX = touchInput.getJmeX(endEvent.getX());
float jmeY = touchInput.invertY(touchInput.getJmeY(endEvent.getY()));
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SCROLL, jmeX, jmeY, touchInput.getJmeX(-distX), touchInput.getJmeY(distY));
touchEvent.setPointerId(touchInput.getPointerId(endEvent));
touchEvent.setTime(endEvent.getEventTime());
touchEvent.setPressure(endEvent.getPressure());
processEvent(touchEvent);
touchInput.addEvent(touchEvent);
}
return true;
}
@Override
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();
// 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[]{touchInput.getPointerId(startEvent), touchInput.getAction(startEvent), startEvent.getX(), startEvent.getY(), touchInput.getAction(endEvent), endEvent.getX(), endEvent.getY(), velocityX, velocityY});
float jmeX = touchInput.getJmeX(startEvent.getX());
float jmeY = touchInput.invertY(touchInput.getJmeY(startEvent.getY()));
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.FLING, jmeX, jmeY, velocityX, velocityY);
touchEvent.setPointerId(getPointerId(endEvent));
touchEvent.setPointerId(touchInput.getPointerId(endEvent));
touchEvent.setTime(endEvent.getEventTime());
touchEvent.setPressure(endEvent.getPressure());
processEvent(touchEvent);
touchInput.addEvent(touchEvent);
return true;
}
/* Events from onDoubleTapListener */
@Override
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();
// logger.log(Level.INFO, "onSingleTapConfirmed pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
float jmeX = touchInput.getJmeX(event.getX());
float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.TAP, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(getPointerId(event));
touchEvent.setPointerId(touchInput.getPointerId(event));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure());
processEvent(touchEvent);
touchInput.addEvent(touchEvent);
return true;
}
@Override
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
// 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();
// logger.log(Level.INFO, "onDoubleTap pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
float jmeX = touchInput.getJmeX(event.getX());
float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.DOUBLETAP, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(getPointerId(event));
touchEvent.setPointerId(touchInput.getPointerId(event));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure());
processEvent(touchEvent);
touchInput.addEvent(touchEvent);
return true;
}
@Override
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);
// }
// logger.log(Level.INFO, "onDoubleTapEvent pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
if (touchInput.getAction(event) == MotionEvent.ACTION_UP) {
TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.DOUBLETAP, event.getX(), touchInput.invertY(event.getY()), 0, 0);
touchEvent.setPointerId(touchInput.getPointerId(event));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure());
touchInput.addEvent(touchEvent);
}
return true;
}
/* Events from ScaleGestureDetector */
@Override
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");
// logger.log(Level.INFO, "onScaleBegin");
scaleStartX = gestureDownX;
scaleStartY = gestureDownY;
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
TouchEvent touchEvent = touchInput.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);
touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
touchInput.addEvent(touchEvent);
return true;
}
@Override
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
// return true or all gestures for this event will be accumulated
logger.log(Level.INFO, "onScale");
// logger.log(Level.INFO, "onScale");
scaleStartX = gestureDownX;
scaleStartY = gestureDownY;
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
TouchEvent touchEvent = touchInput.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);
touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
touchInput.addEvent(touchEvent);
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
logger.log(Level.INFO, "onScaleEnd");
// logger.log(Level.INFO, "onScaleEnd");
scaleStartX = gestureDownX;
scaleStartY = gestureDownY;
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
TouchEvent touchEvent = touchInput.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);
touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
touchInput.addEvent(touchEvent);
}
}

@ -1,686 +0,0 @@
package com.jme3.input.android;
import android.view.*;
import com.jme3.input.KeyInput;
import com.jme3.input.RawInputListener;
import com.jme3.input.TouchInput;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.input.event.TouchEvent;
import com.jme3.input.event.TouchEvent.Type;
import com.jme3.math.Vector2f;
import com.jme3.system.AppSettings;
import com.jme3.util.RingBuffer;
import java.util.HashMap;
import java.util.logging.Logger;
/**
* <code>AndroidInput</code> is one of the main components that connect jme with android. Is derived from GLSurfaceView and handles all Inputs
* @author larynx
*
*/
public class AndroidInput implements
TouchInput,
View.OnTouchListener,
View.OnKeyListener,
GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener,
ScaleGestureDetector.OnScaleGestureListener {
final private static int MAX_EVENTS = 1024;
// Custom settings
public boolean mouseEventsEnabled = true;
public boolean mouseEventsInvertX = false;
public boolean mouseEventsInvertY = false;
public boolean keyboardEventsEnabled = false;
public boolean dontSendHistory = false;
// Used to transfer events from android thread to GLThread
final private RingBuffer<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS);
final private RingBuffer<TouchEvent> eventPoolUnConsumed = new RingBuffer<TouchEvent>(MAX_EVENTS);
final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS);
final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();
// Internal
private View view;
private ScaleGestureDetector scaledetector;
private boolean scaleInProgress = false;
private GestureDetector detector;
private int lastX;
private int lastY;
private final static Logger logger = Logger.getLogger(AndroidInput.class.getName());
private boolean isInitialized = false;
private RawInputListener listener = null;
private static final int[] ANDROID_TO_JME = {
0x0, // unknown
0x0, // key code soft left
0x0, // key code soft right
KeyInput.KEY_HOME,
KeyInput.KEY_ESCAPE, // key back
0x0, // key call
0x0, // key endcall
KeyInput.KEY_0,
KeyInput.KEY_1,
KeyInput.KEY_2,
KeyInput.KEY_3,
KeyInput.KEY_4,
KeyInput.KEY_5,
KeyInput.KEY_6,
KeyInput.KEY_7,
KeyInput.KEY_8,
KeyInput.KEY_9,
KeyInput.KEY_MULTIPLY,
0x0, // key pound
KeyInput.KEY_UP,
KeyInput.KEY_DOWN,
KeyInput.KEY_LEFT,
KeyInput.KEY_RIGHT,
KeyInput.KEY_RETURN, // dpad center
0x0, // volume up
0x0, // volume down
KeyInput.KEY_POWER, // power (?)
0x0, // camera
0x0, // clear
KeyInput.KEY_A,
KeyInput.KEY_B,
KeyInput.KEY_C,
KeyInput.KEY_D,
KeyInput.KEY_E,
KeyInput.KEY_F,
KeyInput.KEY_G,
KeyInput.KEY_H,
KeyInput.KEY_I,
KeyInput.KEY_J,
KeyInput.KEY_K,
KeyInput.KEY_L,
KeyInput.KEY_M,
KeyInput.KEY_N,
KeyInput.KEY_O,
KeyInput.KEY_P,
KeyInput.KEY_Q,
KeyInput.KEY_R,
KeyInput.KEY_S,
KeyInput.KEY_T,
KeyInput.KEY_U,
KeyInput.KEY_V,
KeyInput.KEY_W,
KeyInput.KEY_X,
KeyInput.KEY_Y,
KeyInput.KEY_Z,
KeyInput.KEY_COMMA,
KeyInput.KEY_PERIOD,
KeyInput.KEY_LMENU,
KeyInput.KEY_RMENU,
KeyInput.KEY_LSHIFT,
KeyInput.KEY_RSHIFT,
// 0x0, // fn
// 0x0, // cap (?)
KeyInput.KEY_TAB,
KeyInput.KEY_SPACE,
0x0, // sym (?) symbol
0x0, // explorer
0x0, // envelope
KeyInput.KEY_RETURN, // newline/enter
KeyInput.KEY_DELETE,
KeyInput.KEY_GRAVE,
KeyInput.KEY_MINUS,
KeyInput.KEY_EQUALS,
KeyInput.KEY_LBRACKET,
KeyInput.KEY_RBRACKET,
KeyInput.KEY_BACKSLASH,
KeyInput.KEY_SEMICOLON,
KeyInput.KEY_APOSTROPHE,
KeyInput.KEY_SLASH,
KeyInput.KEY_AT, // at (@)
KeyInput.KEY_NUMLOCK, //0x0, // num
0x0, //headset hook
0x0, //focus
KeyInput.KEY_ADD,
KeyInput.KEY_LMETA, //menu
0x0,//notification
0x0,//search
0x0,//media play/pause
0x0,//media stop
0x0,//media next
0x0,//media previous
0x0,//media rewind
0x0,//media fastforward
0x0,//mute
};
public AndroidInput() {
}
public void setView(View view) {
this.view = view;
if (view != null) {
detector = new GestureDetector(null, this, null, false);
scaledetector = new ScaleGestureDetector(view.getContext(), this);
view.setOnTouchListener(this);
view.setOnKeyListener(this);
}
}
private TouchEvent getNextFreeTouchEvent() {
return getNextFreeTouchEvent(false);
}
/**
* Fetches a touch event from the reuse pool
* @param wait if true waits for a reusable event to get available/released
* by an other thread, if false returns a new one if needed.
*
* @return a usable TouchEvent
*/
private TouchEvent getNextFreeTouchEvent(boolean wait) {
TouchEvent evt = null;
synchronized (eventPoolUnConsumed) {
int size = eventPoolUnConsumed.size();
while (size > 0) {
evt = eventPoolUnConsumed.pop();
if (!evt.isConsumed()) {
eventPoolUnConsumed.push(evt);
evt = null;
} else {
break;
}
size--;
}
}
if (evt == null) {
if (eventPool.isEmpty() && wait) {
logger.warning("eventPool buffer underrun");
boolean isEmpty;
do {
synchronized (eventPool) {
isEmpty = eventPool.isEmpty();
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
} while (isEmpty);
synchronized (eventPool) {
evt = eventPool.pop();
}
} else if (eventPool.isEmpty()) {
evt = new TouchEvent();
logger.warning("eventPool buffer underrun");
} else {
synchronized (eventPool) {
evt = eventPool.pop();
}
}
}
return evt;
}
/**
* onTouch gets called from android thread on touchpad events
*/
public boolean onTouch(View view, MotionEvent event) {
if (view != this.view) {
return false;
}
boolean bWasHandled = false;
TouchEvent touch;
// System.out.println("native : " + event.getAction());
int action = event.getAction() & MotionEvent.ACTION_MASK;
int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
int pointerId = event.getPointerId(pointerIndex);
Vector2f lastPos = lastPositions.get(pointerId);
// final int historySize = event.getHistorySize();
//final int pointerCount = event.getPointerCount();
switch (action) {
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_DOWN:
touch = getNextFreeTouchEvent();
touch.set(Type.DOWN, event.getX(pointerIndex), view.getHeight() - event.getY(pointerIndex), 0, 0);
touch.setPointerId(pointerId);
touch.setTime(event.getEventTime());
touch.setPressure(event.getPressure(pointerIndex));
processEvent(touch);
lastPos = new Vector2f(event.getX(pointerIndex), view.getHeight() - event.getY(pointerIndex));
lastPositions.put(pointerId, lastPos);
bWasHandled = true;
break;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
touch = getNextFreeTouchEvent();
touch.set(Type.UP, event.getX(pointerIndex), view.getHeight() - event.getY(pointerIndex), 0, 0);
touch.setPointerId(pointerId);
touch.setTime(event.getEventTime());
touch.setPressure(event.getPressure(pointerIndex));
processEvent(touch);
lastPositions.remove(pointerId);
bWasHandled = true;
break;
case MotionEvent.ACTION_MOVE:
// Convert all pointers into events
for (int p = 0; p < event.getPointerCount(); p++) {
lastPos = lastPositions.get(event.getPointerId(p));
if (lastPos == null) {
lastPos = new Vector2f(event.getX(p), view.getHeight() - event.getY(p));
lastPositions.put(event.getPointerId(p), lastPos);
}
float dX = event.getX(p) - lastPos.x;
float dY = view.getHeight() - event.getY(p) - lastPos.y;
if (dX != 0 || dY != 0) {
touch = getNextFreeTouchEvent();
touch.set(Type.MOVE, event.getX(p), view.getHeight() - event.getY(p), dX, dY);
touch.setPointerId(event.getPointerId(p));
touch.setTime(event.getEventTime());
touch.setPressure(event.getPressure(p));
touch.setScaleSpanInProgress(scaleInProgress);
processEvent(touch);
lastPos.set(event.getX(p), view.getHeight() - event.getY(p));
}
}
bWasHandled = true;
break;
case MotionEvent.ACTION_OUTSIDE:
break;
}
// Try to detect gestures
this.detector.onTouchEvent(event);
this.scaledetector.onTouchEvent(event);
return bWasHandled;
}
/**
* onKey gets called from android thread on key events
*/
public boolean onKey(View view, int keyCode, KeyEvent event) {
if (view != this.view) {
return false;
}
if (event.getAction() == KeyEvent.ACTION_DOWN) {
TouchEvent evt;
evt = getNextFreeTouchEvent();
evt.set(TouchEvent.Type.KEY_DOWN);
evt.setKeyCode(keyCode);
evt.setCharacters(event.getCharacters());
evt.setTime(event.getEventTime());
// Send the event
processEvent(evt);
// Handle all keys ourself except Volume Up/Down
if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
return false;
} else {
return true;
}
} else if (event.getAction() == KeyEvent.ACTION_UP) {
TouchEvent evt;
evt = getNextFreeTouchEvent();
evt.set(TouchEvent.Type.KEY_UP);
evt.setKeyCode(keyCode);
evt.setCharacters(event.getCharacters());
evt.setTime(event.getEventTime());
// Send the event
processEvent(evt);
// Handle all keys ourself except Volume Up/Down
if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
return false;
} else {
return true;
}
} else {
return false;
}
}
public void loadSettings(AppSettings settings) {
mouseEventsEnabled = settings.isEmulateMouse();
mouseEventsInvertX = settings.isEmulateMouseFlipX();
mouseEventsInvertY = settings.isEmulateMouseFlipY();
}
// -----------------------------------------
// JME3 Input interface
@Override
public void initialize() {
TouchEvent item;
for (int i = 0; i < MAX_EVENTS; i++) {
item = new TouchEvent();
eventPool.push(item);
}
isInitialized = true;
}
@Override
public void destroy() {
isInitialized = false;
// Clean up queues
while (!eventPool.isEmpty()) {
eventPool.pop();
}
while (!eventQueue.isEmpty()) {
eventQueue.pop();
}
this.view = null;
}
@Override
public boolean isInitialized() {
return isInitialized;
}
@Override
public void setInputListener(RawInputListener listener) {
this.listener = listener;
}
@Override
public long getInputTimeNanos() {
return System.nanoTime();
}
// -----------------------------------------
private void processEvent(TouchEvent event) {
synchronized (eventQueue) {
//Discarding events when the ring buffer is full to avoid buffer overflow.
if(eventQueue.size()< MAX_EVENTS){
eventQueue.push(event);
}
}
}
// --------------- INSIDE GLThread ---------------
@Override
public void update() {
generateEvents();
}
private void generateEvents() {
if (listener != null) {
TouchEvent event;
MouseButtonEvent btn;
MouseMotionEvent mot;
int newX;
int newY;
while (!eventQueue.isEmpty()) {
synchronized (eventQueue) {
event = eventQueue.pop();
}
if (event != null) {
listener.onTouchEvent(event);
if (mouseEventsEnabled) {
if (mouseEventsInvertX) {
newX = view.getWidth() - (int) event.getX();
} else {
newX = (int) event.getX();
}
if (mouseEventsInvertY) {
newY = view.getHeight() - (int) event.getY();
} else {
newY = (int) event.getY();
}
switch (event.getType()) {
case DOWN:
// Handle mouse down event
btn = new MouseButtonEvent(0, true, newX, newY);
btn.setTime(event.getTime());
listener.onMouseButtonEvent(btn);
// Store current pos
lastX = -1;
lastY = -1;
break;
case UP:
// Handle mouse up event
btn = new MouseButtonEvent(0, false, newX, newY);
btn.setTime(event.getTime());
listener.onMouseButtonEvent(btn);
// Store current pos
lastX = -1;
lastY = -1;
break;
case SCALE_MOVE:
if (lastX != -1 && lastY != -1) {
newX = lastX;
newY = lastY;
}
int wheel = (int) (event.getScaleSpan() / 4f); // scale to match mouse wheel
int dwheel = (int) (event.getDeltaScaleSpan() / 4f); // scale to match mouse wheel
mot = new MouseMotionEvent(newX, newX, 0, 0, wheel, dwheel);
mot.setTime(event.getTime());
listener.onMouseMotionEvent(mot);
lastX = newX;
lastY = newY;
break;
case MOVE:
if (event.isScaleSpanInProgress()) {
break;
}
int dx;
int dy;
if (lastX != -1) {
dx = newX - lastX;
dy = newY - lastY;
} else {
dx = 0;
dy = 0;
}
mot = new MouseMotionEvent(newX, newY, dx, dy, (int)event.getScaleSpan(), (int)event.getDeltaScaleSpan());
mot.setTime(event.getTime());
listener.onMouseMotionEvent(mot);
lastX = newX;
lastY = newY;
break;
}
}
}
if (event.isConsumed() == false) {
synchronized (eventPoolUnConsumed) {
eventPoolUnConsumed.push(event);
}
} else {
synchronized (eventPool) {
eventPool.push(event);
}
}
}
}
}
// --------------- ENDOF INSIDE GLThread ---------------
// --------------- Gesture detected callback events ---------------
public boolean onDown(MotionEvent event) {
return false;
}
public void onLongPress(MotionEvent event) {
TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.LONGPRESSED, event.getX(), view.getHeight() - event.getY(), 0f, 0f);
touch.setPointerId(0);
touch.setTime(event.getEventTime());
processEvent(touch);
}
public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) {
TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.FLING, event.getX(), view.getHeight() - event.getY(), vx, vy);
touch.setPointerId(0);
touch.setTime(event.getEventTime());
processEvent(touch);
return true;
}
public boolean onSingleTapConfirmed(MotionEvent event) {
//Nothing to do here the tap has already been detected.
return false;
}
public boolean onDoubleTap(MotionEvent event) {
TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.DOUBLETAP, event.getX(), view.getHeight() - event.getY(), 0f, 0f);
touch.setPointerId(0);
touch.setTime(event.getEventTime());
processEvent(touch);
return true;
}
public boolean onDoubleTapEvent(MotionEvent event) {
return false;
}
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
scaleInProgress = true;
TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.SCALE_START, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f);
touch.setPointerId(0);
touch.setTime(scaleGestureDetector.getEventTime());
touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
touch.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
touch.setScaleSpanInProgress(scaleInProgress);
processEvent(touch);
// System.out.println("scaleBegin");
return true;
}
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), view.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
touch.setPointerId(0);
touch.setTime(scaleGestureDetector.getEventTime());
touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
touch.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
touch.setScaleSpanInProgress(scaleInProgress);
processEvent(touch);
// System.out.println("scale");
return false;
}
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
scaleInProgress = false;
TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), view.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
touch.setPointerId(0);
touch.setTime(scaleGestureDetector.getEventTime());
touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
touch.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
touch.setScaleSpanInProgress(scaleInProgress);
processEvent(touch);
}
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.SCROLL, e1.getX(), view.getHeight() - e1.getY(), distanceX, distanceY * (-1));
touch.setPointerId(0);
touch.setTime(e1.getEventTime());
processEvent(touch);
//System.out.println("scroll " + e1.getPointerCount());
return false;
}
public void onShowPress(MotionEvent event) {
TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.SHOWPRESS, event.getX(), view.getHeight() - event.getY(), 0f, 0f);
touch.setPointerId(0);
touch.setTime(event.getEventTime());
processEvent(touch);
}
public boolean onSingleTapUp(MotionEvent event) {
TouchEvent touch = getNextFreeTouchEvent();
touch.set(Type.TAP, event.getX(), view.getHeight() - event.getY(), 0f, 0f);
touch.setPointerId(0);
touch.setTime(event.getEventTime());
processEvent(touch);
return true;
}
@Override
public void setSimulateKeyboard(boolean simulate) {
keyboardEventsEnabled = simulate;
}
@Override
public void setOmitHistoricEvents(boolean dontSendHistory) {
this.dontSendHistory = dontSendHistory;
}
/**
* @deprecated Use {@link #getSimulateMouse()};
*/
@Deprecated
public boolean isMouseEventsEnabled() {
return mouseEventsEnabled;
}
@Deprecated
public void setMouseEventsEnabled(boolean mouseEventsEnabled) {
this.mouseEventsEnabled = mouseEventsEnabled;
}
public boolean isMouseEventsInvertY() {
return mouseEventsInvertY;
}
public void setMouseEventsInvertY(boolean mouseEventsInvertY) {
this.mouseEventsInvertY = mouseEventsInvertY;
}
public boolean isMouseEventsInvertX() {
return mouseEventsInvertX;
}
public void setMouseEventsInvertX(boolean mouseEventsInvertX) {
this.mouseEventsInvertX = mouseEventsInvertX;
}
public void setSimulateMouse(boolean simulate) {
mouseEventsEnabled = simulate;
}
public boolean getSimulateMouse() {
return isSimulateMouse();
}
public boolean isSimulateMouse() {
return mouseEventsEnabled;
}
public boolean isSimulateKeyboard() {
return keyboardEventsEnabled;
}
}

@ -33,231 +33,206 @@
package com.jme3.input.android;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.view.GestureDetector;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import com.jme3.input.RawInputListener;
import com.jme3.input.JoyInput;
import com.jme3.input.TouchInput;
import com.jme3.input.event.InputEvent;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.input.event.TouchEvent;
import com.jme3.system.AppSettings;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* <code>AndroidInput</code> is the main class that connects the Android system
* inputs to jME. It serves as the manager that gathers inputs from the various
* Android input methods and provides them to jME's <code>InputManager</code>.
* inputs to jME. It receives the inputs from the Android View and passes them
* to the appropriate classes based on the source of the input.</br>
* This class is to be extended when new functionality is released in Android.
*
* @author iwgeric
*/
public class AndroidInputHandler implements TouchInput {
private static final Logger logger = Logger.getLogger(AndroidInputHandler.class.getName());
// Custom settings
private boolean mouseEventsEnabled = true;
private boolean mouseEventsInvertX = false;
private boolean mouseEventsInvertY = false;
private boolean keyboardEventsEnabled = false;
private boolean dontSendHistory = false;
public class AndroidInputHandler implements View.OnTouchListener,
View.OnKeyListener {
private static final Logger logger = Logger.getLogger(AndroidInputHandler.class.getName());
// Internal
private GLSurfaceView view;
private AndroidTouchHandler touchHandler;
private AndroidKeyHandler keyHandler;
private AndroidGestureHandler gestureHandler;
private boolean initialized = false;
private RawInputListener listener = null;
private ConcurrentLinkedQueue<InputEvent> inputEventQueue = new ConcurrentLinkedQueue<InputEvent>();
private final static int MAX_TOUCH_EVENTS = 1024;
private final TouchEventPool touchEventPool = new TouchEventPool(MAX_TOUCH_EVENTS);
private float scaleX = 1f;
private float scaleY = 1f;
protected GLSurfaceView view;
protected AndroidTouchInput touchInput;
protected AndroidJoyInput joyInput;
public AndroidInputHandler() {
int buildVersion = Build.VERSION.SDK_INT;
logger.log(Level.INFO, "Android Build Version: {0}", buildVersion);
if (buildVersion >= 14) {
// add support for onHover and GenericMotionEvent (ie. gamepads)
gestureHandler = new AndroidGestureHandler(this);
touchHandler = new AndroidTouchHandler14(this, gestureHandler);
keyHandler = new AndroidKeyHandler(this);
} else if (buildVersion >= 8){
gestureHandler = new AndroidGestureHandler(this);
touchHandler = new AndroidTouchHandler(this, gestureHandler);
keyHandler = new AndroidKeyHandler(this);
}
}
public AndroidInputHandler(AndroidTouchHandler touchInput,
AndroidKeyHandler keyInput, AndroidGestureHandler gestureHandler) {
this.touchHandler = touchInput;
this.keyHandler = keyInput;
this.gestureHandler = gestureHandler;
touchInput = new AndroidTouchInput(this);
joyInput = new AndroidJoyInput(this);
}
public void setView(View view) {
if (touchHandler != null) {
touchHandler.setView(view);
if (this.view != null && view != null && this.view.equals(view)) {
return;
}
if (keyHandler != null) {
keyHandler.setView(view);
}
if (gestureHandler != null) {
gestureHandler.setView(view);
if (this.view != null) {
removeListeners(this.view);
}
this.view = (GLSurfaceView)view;
}
public View getView() {
return view;
}
if (this.view != null) {
addListeners(this.view);
}
public float invertX(float origX) {
return getJmeX(view.getWidth()) - origX;
joyInput.setView((GLSurfaceView)view);
}
public float invertY(float origY) {
return getJmeY(view.getHeight()) - origY;
public View getView() {
return view;
}
public float getJmeX(float origX) {
return origX * scaleX;
protected void removeListeners(GLSurfaceView view) {
view.setOnTouchListener(null);
view.setOnKeyListener(null);
touchInput.setGestureDetector(null);
touchInput.setScaleDetector(null);
}
public float getJmeY(float origY) {
return origY * scaleY;
protected void addListeners(GLSurfaceView view) {
view.setOnTouchListener(this);
view.setOnKeyListener(this);
AndroidGestureProcessor gestureHandler = new AndroidGestureProcessor(touchInput);
touchInput.setGestureDetector(new GestureDetector(
view.getContext(), gestureHandler));
touchInput.setScaleDetector(new ScaleGestureDetector(
view.getContext(), gestureHandler));
}
public void loadSettings(AppSettings settings) {
keyboardEventsEnabled = settings.isEmulateKeyboard();
mouseEventsEnabled = settings.isEmulateMouse();
mouseEventsInvertX = settings.isEmulateMouseFlipX();
mouseEventsInvertY = settings.isEmulateMouseFlipY();
// view width and height are 0 until the view is displayed on the screen
if (view.getWidth() != 0 && view.getHeight() != 0) {
scaleX = (float)settings.getWidth() / (float)view.getWidth();
scaleY = (float)settings.getHeight() / (float)view.getHeight();
}
logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}",
new Object[]{scaleX, scaleY});
touchInput.loadSettings(settings);
}
public TouchInput getTouchInput() {
return touchInput;
}
public JoyInput getJoyInput() {
return joyInput;
}
/*
* Android input events include the source from which the input came from.
* We must look at the source of the input event to determine which type
* of jME input it belongs to.
* If the input is from a gamepad or joystick source, the event is sent
* to the JoyInput class to convert the event into jME joystick events.
* </br>
* If the input is from a touchscreen source, the event is sent to the
* TouchProcessor to convert the event into touch events.
* The TouchProcessor also converts the events into Mouse and Key events
* if AppSettings is set to simulate Mouse or Keyboard events.
*
* Android reports the source as a bitmask as shown below.</br>
*
* InputDevice Sources
* 0000 0000 0000 0000 0000 0000 0000 0000 - 32 bit bitmask
*
* 0000 0000 0000 0000 0000 0000 1111 1111 - SOURCE_CLASS_MASK (0x000000ff)
* 0000 0000 0000 0000 0000 0000 0000 0000 - SOURCE_CLASS_NONE (0x00000000)
* 0000 0000 0000 0000 0000 0000 0000 0001 - SOURCE_CLASS_BUTTON (0x00000001)
* 0000 0000 0000 0000 0000 0000 0000 0010 - SOURCE_CLASS_POINTER (0x00000002)
* 0000 0000 0000 0000 0000 0000 0000 0100 - SOURCE_CLASS_TRACKBALL (0x00000004)
* 0000 0000 0000 0000 0000 0000 0000 1000 - SOURCE_CLASS_POSITION (0x00000008)
* 0000 0000 0000 0000 0000 0000 0001 0000 - SOURCE_CLASS_JOYSTICK (0x00000010)
*
* 1111 1111 1111 1111 1111 1111 0000 0000 - Source_Any (0xffffff00)
* 0000 0000 0000 0000 0000 0000 0000 0000 - SOURCE_UNKNOWN (0x00000000)
* 0000 0000 0000 0000 0000 0001 0000 0001 - SOURCE_KEYBOARD (0x00000101)
* 0000 0000 0000 0000 0000 0010 0000 0001 - SOURCE_DPAD (0x00000201)
* 0000 0000 0000 0000 0000 0100 0000 0001 - SOURCE_GAMEPAD (0x00000401)
* 0000 0000 0000 0000 0001 0000 0000 0010 - SOURCE_TOUCHSCREEN (0x00001002)
* 0000 0000 0000 0000 0010 0000 0000 0010 - SOURCE_MOUSE (0x00002002)
* 0000 0000 0000 0000 0100 0000 0000 0010 - SOURCE_STYLUS (0x00004002)
* 0000 0000 0000 0001 0000 0000 0000 0100 - SOURCE_TRACKBALL (0x00010004)
* 0000 0000 0001 0000 0000 0000 0000 1000 - SOURCE_TOUCHPAD (0x00100008)
* 0000 0000 0010 0000 0000 0000 0000 0000 - SOURCE_TOUCH_NAVIGATION (0x00200000)
* 0000 0001 0000 0000 0000 0000 0001 0000 - SOURCE_JOYSTICK (0x01000010)
* 0000 0010 0000 0000 0000 0000 0000 0001 - SOURCE_HDMI (0x02000001)
*
* Example values reported by Android for Source
* 4,098 = 0x00001002 =
* 0000 0000 0000 0000 0001 0000 0000 0010 - SOURCE_CLASS_POINTER
* SOURCE_TOUCHSCREEN
* 1,281 = 0x00000501 =
* 0000 0000 0000 0000 0000 0101 0000 0001 - SOURCE_CLASS_BUTTON
* SOURCE_KEYBOARD
* SOURCE_GAMEPAD
* 16,777,232 = 0x01000010 =
* 0000 0001 0000 0000 0000 0000 0001 0000 - SOURCE_CLASS_JOYSTICK
* SOURCE_JOYSTICK
*
* 16,778,513 = 0x01000511 =
* 0000 0001 0000 0000 0000 0101 0001 0001 - SOURCE_CLASS_BUTTON
* SOURCE_CLASS_JOYSTICK
* SOURCE_GAMEPAD
* SOURCE_KEYBOARD
* SOURCE_JOYSTICK
*
* 257 = 0x00000101 =
* 0000 0000 0000 0000 0000 0001 0000 0001 - SOURCE_CLASS_BUTTON
* SOURCE_KEYBOARD
*
*
*
*/
}
// -----------------------------------------
// JME3 Input interface
@Override
public void initialize() {
touchEventPool.initialize();
if (touchHandler != null) {
touchHandler.initialize();
}
if (keyHandler != null) {
keyHandler.initialize();
}
if (gestureHandler != null) {
gestureHandler.initialize();
public boolean onTouch(View view, MotionEvent event) {
if (view != getView()) {
return false;
}
initialized = true;
}
boolean consumed = false;
@Override
public void destroy() {
initialized = false;
int source = event.getSource();
// logger.log(Level.INFO, "onTouch source: {0}", source);
touchEventPool.destroy();
if (touchHandler != null) {
touchHandler.destroy();
}
if (keyHandler != null) {
keyHandler.destroy();
}
if (gestureHandler != null) {
gestureHandler.destroy();
}
setView(null);
}
@Override
public boolean isInitialized() {
return initialized;
}
@Override
public void setInputListener(RawInputListener listener) {
this.listener = listener;
}
@Override
public long getInputTimeNanos() {
return System.nanoTime();
}
boolean isTouch = ((source & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN);
// logger.log(Level.INFO, "onTouch source: {0}, isTouch: {1}",
// new Object[]{source, isTouch});
public void update() {
if (listener != null) {
InputEvent inputEvent;
while ((inputEvent = inputEventQueue.poll()) != null) {
if (inputEvent instanceof TouchEvent) {
listener.onTouchEvent((TouchEvent)inputEvent);
} else if (inputEvent instanceof MouseButtonEvent) {
listener.onMouseButtonEvent((MouseButtonEvent)inputEvent);
} else if (inputEvent instanceof MouseMotionEvent) {
listener.onMouseMotionEvent((MouseMotionEvent)inputEvent);
} else if (inputEvent instanceof KeyInputEvent) {
listener.onKeyEvent((KeyInputEvent)inputEvent);
}
}
if (isTouch && touchInput != null) {
// send the event to the touch processor
consumed = touchInput.onTouch(event);
}
}
// -----------------------------------------
return consumed;
public TouchEvent getFreeTouchEvent() {
return touchEventPool.getNextFreeEvent();
}
public void addEvent(InputEvent event) {
inputEventQueue.add(event);
if (event instanceof TouchEvent) {
touchEventPool.storeEvent((TouchEvent)event);
@Override
public boolean onKey(View view, int keyCode, KeyEvent event) {
if (view != getView()) {
return false;
}
}
public void setSimulateMouse(boolean simulate) {
this.mouseEventsEnabled = simulate;
}
boolean consumed = false;
public boolean isSimulateMouse() {
return mouseEventsEnabled;
}
int source = event.getSource();
// logger.log(Level.INFO, "onKey source: {0}", source);
public boolean isMouseEventsInvertX() {
return mouseEventsInvertX;
}
boolean isTouch =
((source & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) ||
((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD);
// logger.log(Level.INFO, "onKey source: {0}, isTouch: {1}",
// new Object[]{source, isTouch});
public boolean isMouseEventsInvertY() {
return mouseEventsInvertY;
}
public void setSimulateKeyboard(boolean simulate) {
this.keyboardEventsEnabled = simulate;
}
if (touchInput != null) {
consumed = touchInput.onKey(event);
}
public boolean isSimulateKeyboard() {
return keyboardEventsEnabled;
}
return consumed;
public void setOmitHistoricEvents(boolean dontSendHistory) {
this.dontSendHistory = dontSendHistory;
}
}

@ -0,0 +1,158 @@
/*
* 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.opengl.GLSurfaceView;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* <code>AndroidInputHandler14</code> extends <code>AndroidInputHandler</code> to
* add the onHover and onGenericMotion events that where added in Android rev 14 (Android 4.0).</br>
* The onGenericMotion events are the main interface to Joystick axes. They
* were actually released in Android rev 12.
*
* @author iwgeric
*/
public class AndroidInputHandler14 extends AndroidInputHandler implements View.OnHoverListener,
View.OnGenericMotionListener {
private static final Logger logger = Logger.getLogger(AndroidInputHandler14.class.getName());
public AndroidInputHandler14() {
touchInput = new AndroidTouchInput14(this);
joyInput = new AndroidJoyInput14(this);
}
@Override
protected void removeListeners(GLSurfaceView view) {
super.removeListeners(view);
view.setOnHoverListener(null);
view.setOnGenericMotionListener(null);
}
@Override
protected void addListeners(GLSurfaceView view) {
super.addListeners(view);
view.setOnHoverListener(this);
view.setOnGenericMotionListener(this);
}
@Override
public boolean onHover(View view, MotionEvent event) {
if (view != getView()) {
return false;
}
boolean consumed = false;
int source = event.getSource();
// logger.log(Level.INFO, "onTouch source: {0}", source);
boolean isTouch = ((source & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN);
// logger.log(Level.INFO, "onTouch source: {0}, isTouch: {1}",
// new Object[]{source, isTouch});
if (isTouch && touchInput != null) {
// send the event to the touch processor
consumed = ((AndroidTouchInput14)touchInput).onHover(event);
}
return consumed;
}
@Override
public boolean onGenericMotion(View view, MotionEvent event) {
if (view != getView()) {
return false;
}
boolean consumed = false;
int source = event.getSource();
// logger.log(Level.INFO, "onGenericMotion source: {0}", source);
boolean isJoystick =
((source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) ||
((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK);
if (isJoystick && joyInput != null) {
// logger.log(Level.INFO, "onGenericMotion source: {0}, isJoystick: {1}",
// new Object[]{source, isJoystick});
// send the event to the touch processor
consumed = consumed || ((AndroidJoyInput14)joyInput).onGenericMotion(event);
}
return consumed;
}
@Override
public boolean onKey(View view, int keyCode, KeyEvent event) {
if (view != getView()) {
return false;
}
boolean consumed = false;
// logger.log(Level.INFO, "onKey keyCode: {0}, action: {1}, event: {2}",
// new Object[]{KeyEvent.keyCodeToString(keyCode), event.getAction(), event});
int source = event.getSource();
// logger.log(Level.INFO, "onKey source: {0}", source);
boolean isTouch =
((source & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) ||
((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD);
boolean isJoystick =
((source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) ||
((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK);
if (isTouch && touchInput != null) {
// logger.log(Level.INFO, "onKey source: {0}, isTouch: {1}",
// new Object[]{source, isTouch});
consumed = touchInput.onKey(event);
}
if (isJoystick && joyInput != null) {
// logger.log(Level.INFO, "onKey source: {0}, isJoystick: {1}",
// new Object[]{source, isJoystick});
consumed = consumed || ((AndroidJoyInput14)joyInput).onKey(event);
}
return consumed;
}
}

@ -33,9 +33,7 @@ package com.jme3.input.android;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Vibrator;
import android.view.View;
import com.jme3.input.InputManager;
import com.jme3.input.JoyInput;
import com.jme3.input.Joystick;
@ -79,15 +77,15 @@ import java.util.logging.Logger;
*
* @author iwgeric
*/
public class AndroidJoyInputHandler implements JoyInput {
private static final Logger logger = Logger.getLogger(AndroidJoyInputHandler.class.getName());
public class AndroidJoyInput implements JoyInput {
private static final Logger logger = Logger.getLogger(AndroidJoyInput.class.getName());
private List<Joystick> joystickList = new ArrayList<Joystick>();
protected AndroidInputHandler inputHandler;
protected List<Joystick> joystickList = new ArrayList<Joystick>();
// private boolean dontSendHistory = false;
// Internal
private GLSurfaceView view;
private boolean initialized = false;
private RawInputListener listener = null;
private ConcurrentLinkedQueue<InputEvent> eventQueue = new ConcurrentLinkedQueue<InputEvent>();
@ -96,34 +94,29 @@ public class AndroidJoyInputHandler implements JoyInput {
private boolean vibratorActive = false;
private long maxRumbleTime = 250; // 250ms
public AndroidJoyInputHandler() {
int buildVersion = Build.VERSION.SDK_INT;
logger.log(Level.INFO, "Android Build Version: {0}", buildVersion);
// if (buildVersion >= 14) {
// touchHandler = new AndroidTouchHandler14(this);
// } else if (buildVersion >= 8){
// touchHandler = new AndroidTouchHandler(this);
// }
public AndroidJoyInput(AndroidInputHandler inputHandler) {
this.inputHandler = inputHandler;
sensorJoyInput = new AndroidSensorJoyInput(this);
}
public void setView(GLSurfaceView view) {
// if (touchHandler != null) {
// touchHandler.setView(view);
// }
if (view == null) {
vibrator = null;
} else {
// Get instance of Vibrator from current Context
vibrator = (Vibrator) view.getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator == null) {
logger.log(Level.FINE, "Vibrator Service not found.");
}
}
if (sensorJoyInput != null) {
sensorJoyInput.setView(view);
}
this.view = (GLSurfaceView)view;
}
public View getView() {
return view;
}
public void loadSettings(AppSettings settings) {
// sensorEventsEnabled = settings.useSensors();
}
public void addEvent(InputEvent event) {
@ -155,20 +148,8 @@ public class AndroidJoyInputHandler implements JoyInput {
}
@Override
public void initialize() {
// if (sensorJoyInput != null) {
// sensorJoyInput.initialize();
// }
// Get instance of Vibrator from current Context
vibrator = (Vibrator) view.getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator == null) {
logger.log(Level.FINE, "Vibrator Service not found.");
}
initialized = true;
}
@ -211,8 +192,8 @@ public class AndroidJoyInputHandler implements JoyInput {
};
final int rumbleRepeatFrom = 0; // index into rumble pattern to repeat from
logger.log(Level.FINE, "Rumble amount: {0}, rumbleOnDur: {1}, rumbleOffDur: {2}",
new Object[]{amount, rumbleOnDur, rumbleOffDur});
// logger.log(Level.FINE, "Rumble amount: {0}, rumbleOnDur: {1}, rumbleOffDur: {2}",
// new Object[]{amount, rumbleOnDur, rumbleOffDur});
if (rumbleOnDur > 0) {
vibrator.vibrate(rumblePattern, rumbleRepeatFrom);
@ -226,9 +207,8 @@ public class AndroidJoyInputHandler implements JoyInput {
@Override
public Joystick[] loadJoysticks(InputManager inputManager) {
logger.log(Level.INFO, "loading joysticks for {0}", this.getClass().getName());
joystickList.add(sensorJoyInput.loadJoystick(joystickList.size(), inputManager));
return joystickList.toArray( new Joystick[joystickList.size()] );
}
@ -252,6 +232,4 @@ public class AndroidJoyInputHandler implements JoyInput {
}
}

@ -0,0 +1,108 @@
/*
* Copyright (c) 2009-2015 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.KeyEvent;
import android.view.MotionEvent;
import com.jme3.input.InputManager;
import com.jme3.input.Joystick;
import java.util.logging.Logger;
/**
* <code>AndroidJoyInput14</code> extends <code>AndroidJoyInput</code>
* to include support for physical joysticks/gamepads.</br>
*
* @author iwgeric
*/
public class AndroidJoyInput14 extends AndroidJoyInput {
private static final Logger logger = Logger.getLogger(AndroidJoyInput14.class.getName());
private AndroidJoystickJoyInput14 joystickJoyInput;
public AndroidJoyInput14(AndroidInputHandler inputHandler) {
super(inputHandler);
joystickJoyInput = new AndroidJoystickJoyInput14(this);
}
/**
* Pauses the joystick device listeners to save battery life if they are not needed.
* Used to pause when the activity pauses
*/
@Override
public void pauseJoysticks() {
super.pauseJoysticks();
if (joystickJoyInput != null) {
joystickJoyInput.pauseJoysticks();
}
}
/**
* Resumes the joystick device listeners.
* Used to resume when the activity comes to the top of the stack
*/
@Override
public void resumeJoysticks() {
super.resumeJoysticks();
if (joystickJoyInput != null) {
joystickJoyInput.resumeJoysticks();
}
}
@Override
public void destroy() {
super.destroy();
if (joystickJoyInput != null) {
joystickJoyInput.destroy();
}
}
@Override
public Joystick[] loadJoysticks(InputManager inputManager) {
// load the simulated joystick for device orientation
super.loadJoysticks(inputManager);
// load physical gamepads/joysticks
joystickList.addAll(joystickJoyInput.loadJoysticks(joystickList.size(), inputManager));
// return the list of joysticks back to InputManager
return joystickList.toArray( new Joystick[joystickList.size()] );
}
public boolean onGenericMotion(MotionEvent event) {
return joystickJoyInput.onGenericMotion(event);
}
public boolean onKey(KeyEvent event) {
return joystickJoyInput.onKey(event);
}
}

@ -0,0 +1,403 @@
/*
* Copyright (c) 2009-2015 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.InputDevice;
import android.view.InputDevice.MotionRange;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import com.jme3.input.AbstractJoystick;
import com.jme3.input.DefaultJoystickAxis;
import com.jme3.input.DefaultJoystickButton;
import com.jme3.input.InputManager;
import com.jme3.input.JoyInput;
import com.jme3.input.Joystick;
import com.jme3.input.JoystickAxis;
import com.jme3.input.JoystickButton;
import com.jme3.input.event.JoyAxisEvent;
import com.jme3.input.event.JoyButtonEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Main class that creates and manages Android inputs for physical gamepads/joysticks.
*
* @author iwgeric
*/
public class AndroidJoystickJoyInput14 {
private static final Logger logger = Logger.getLogger(AndroidJoystickJoyInput14.class.getName());
private boolean loaded = false;
private AndroidJoyInput joyInput;
private Map<Integer, AndroidJoystick> joystickIndex = new HashMap<Integer, AndroidJoystick>();
private static int[] AndroidGamepadButtons = {
// Dpad buttons
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
KeyEvent.KEYCODE_DPAD_CENTER,
// pressing joystick down
KeyEvent.KEYCODE_BUTTON_THUMBL, KeyEvent.KEYCODE_BUTTON_THUMBR,
// buttons
KeyEvent.KEYCODE_BUTTON_A, KeyEvent.KEYCODE_BUTTON_B,
KeyEvent.KEYCODE_BUTTON_X, KeyEvent.KEYCODE_BUTTON_Y,
// buttons on back of device
KeyEvent.KEYCODE_BUTTON_L1, KeyEvent.KEYCODE_BUTTON_R1,
KeyEvent.KEYCODE_BUTTON_L2, KeyEvent.KEYCODE_BUTTON_R2,
// start / select buttons
KeyEvent.KEYCODE_BUTTON_START, KeyEvent.KEYCODE_BUTTON_SELECT,
KeyEvent.KEYCODE_BUTTON_MODE,
};
public AndroidJoystickJoyInput14(AndroidJoyInput joyInput) {
this.joyInput = joyInput;
}
public void pauseJoysticks() {
}
public void resumeJoysticks() {
}
public void destroy() {
}
public List<Joystick> loadJoysticks(int joyId, InputManager inputManager) {
logger.log(Level.INFO, "loading Joystick devices");
ArrayList<Joystick> joysticks = new ArrayList<Joystick>();
joysticks.clear();
joystickIndex.clear();
ArrayList gameControllerDeviceIds = new ArrayList();
int[] deviceIds = InputDevice.getDeviceIds();
for (int deviceId : deviceIds) {
InputDevice dev = InputDevice.getDevice(deviceId);
int sources = dev.getSources();
logger.log(Level.FINE, "deviceId[{0}] sources: {1}", new Object[]{deviceId, sources});
// Verify that the device has gamepad buttons, control sticks, or both.
if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) ||
((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) {
// This device is a game controller. Store its device ID.
if (!gameControllerDeviceIds.contains(deviceId)) {
gameControllerDeviceIds.add(deviceId);
logger.log(Level.FINE, "Attempting to create joystick for device: {0}", dev);
// Create an AndroidJoystick and store the InputDevice so we
// can later correspond the input from the InputDevice to the
// appropriate jME Joystick event
AndroidJoystick joystick = new AndroidJoystick(inputManager,
joyInput,
dev,
joyId+joysticks.size(),
dev.getName());
joystickIndex.put(deviceId, joystick);
joysticks.add(joystick);
// Each analog input is reported as a MotionRange
// The axis number corresponds to the type of axis
// The AndroidJoystick.addAxis(MotionRange) converts the axis
// type reported by Android into the jME Joystick axis
List<MotionRange> motionRanges = dev.getMotionRanges();
for (MotionRange motionRange: motionRanges) {
logger.log(Level.INFO, "motion range: {0}", motionRange.toString());
logger.log(Level.INFO, "axis: {0}", motionRange.getAxis());
JoystickAxis axis = joystick.addAxis(motionRange);
logger.log(Level.INFO, "added axis: {0}", axis);
}
// InputDevice has a method for determining if a keyCode is
// supported (InputDevice public boolean[] hasKeys (int... keys)).
// But this method wasn't added until rev 19 (Android 4.4)
// Therefore, we only can query the entire device and see if
// any InputDevice supports the keyCode. This may result in
// buttons being configured that don't exist on the specific
// device, but I haven't found a better way yet.
for (int keyCode: AndroidGamepadButtons) {
logger.log(Level.INFO, "button[{0}]: {1}",
new Object[]{keyCode, KeyCharacterMap.deviceHasKey(keyCode)});
if (KeyCharacterMap.deviceHasKey(keyCode)) {
// add button even though we aren't sure if the button
// actually exists on this InputDevice
logger.log(Level.INFO, "button[{0}] exists somewhere", keyCode);
JoystickButton button = joystick.addButton(keyCode);
logger.log(Level.INFO, "added button: {0}", button);
}
}
}
}
}
loaded = true;
return joysticks;
}
public boolean onGenericMotion(MotionEvent event) {
boolean consumed = false;
// logger.log(Level.INFO, "onGenericMotion event: {0}", event);
event.getDeviceId();
event.getSource();
// logger.log(Level.INFO, "deviceId: {0}, source: {1}", new Object[]{event.getDeviceId(), event.getSource()});
AndroidJoystick joystick = joystickIndex.get(event.getDeviceId());
if (joystick != null) {
for (int androidAxis: joystick.getAndroidAxes()) {
String axisName = MotionEvent.axisToString(androidAxis);
float value = event.getAxisValue(androidAxis);
int action = event.getAction();
if (action == MotionEvent.ACTION_MOVE) {
// logger.log(Level.INFO, "MOVE axis num: {0}, axisName: {1}, value: {2}",
// new Object[]{androidAxis, axisName, value});
JoystickAxis axis = joystick.getAxis(androidAxis);
if (axis != null) {
// logger.log(Level.INFO, "MOVE axis num: {0}, axisName: {1}, value: {2}, deadzone: {3}",
// new Object[]{androidAxis, axisName, value, axis.getDeadZone()});
JoyAxisEvent axisEvent = new JoyAxisEvent(axis, value);
joyInput.addEvent(axisEvent);
consumed = true;
} else {
// logger.log(Level.INFO, "axis was null for axisName: {0}", axisName);
}
} else {
// logger.log(Level.INFO, "action: {0}", action);
}
}
}
return consumed;
}
public boolean onKey(KeyEvent event) {
boolean consumed = false;
// logger.log(Level.INFO, "onKey event: {0}", event);
event.getDeviceId();
event.getSource();
AndroidJoystick joystick = joystickIndex.get(event.getDeviceId());
if (joystick != null) {
JoystickButton button = joystick.getButton(event.getKeyCode());
if (button != null) {
boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN;
JoyButtonEvent buttonEvent = new JoyButtonEvent(button, pressed);
joyInput.addEvent(buttonEvent);
consumed = true;
}
}
return consumed;
}
protected class AndroidJoystick extends AbstractJoystick {
private JoystickAxis nullAxis;
private InputDevice device;
private JoystickAxis xAxis;
private JoystickAxis yAxis;
private JoystickAxis povX;
private JoystickAxis povY;
private Map<Integer, JoystickAxis> axisIndex = new HashMap<Integer, JoystickAxis>();
private Map<Integer, JoystickButton> buttonIndex = new HashMap<Integer, JoystickButton>();
public AndroidJoystick( InputManager inputManager, JoyInput joyInput, InputDevice device,
int joyId, String name ) {
super( inputManager, joyInput, joyId, name );
this.device = device;
this.nullAxis = new DefaultJoystickAxis( getInputManager(), this, -1,
"Null", "null", false, false, 0 );
this.xAxis = nullAxis;
this.yAxis = nullAxis;
this.povX = nullAxis;
this.povY = nullAxis;
}
protected JoystickAxis getAxis(int androidAxis) {
return axisIndex.get(androidAxis);
}
protected Set<Integer> getAndroidAxes() {
return axisIndex.keySet();
}
protected JoystickButton getButton(int keyCode) {
return buttonIndex.get(keyCode);
}
protected JoystickButton addButton( int keyCode ) {
// logger.log(Level.FINE, "Adding button: {0}", keyCode);
String name = KeyEvent.keyCodeToString(keyCode);
String logicalId = KeyEvent.keyCodeToString(keyCode);
// A/B/X/Y buttons
if (keyCode == KeyEvent.KEYCODE_BUTTON_Y) {
logicalId = JoystickButton.BUTTON_0;
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_A) {
logicalId = JoystickButton.BUTTON_2;
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_B) {
logicalId = JoystickButton.BUTTON_1;
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_X) {
logicalId = JoystickButton.BUTTON_3;
// Front buttons Some of these have the top ones and the bottoms ones flipped.
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_L1) {
logicalId = JoystickButton.BUTTON_4;
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_R1) {
logicalId = JoystickButton.BUTTON_5;
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_L2) {
logicalId = JoystickButton.BUTTON_6;
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_R2) {
logicalId = JoystickButton.BUTTON_7;
// // Dpad buttons
// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
// logicalId = JoystickButton.BUTTON_8;
// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
// logicalId = JoystickButton.BUTTON_9;
// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
// logicalId = JoystickButton.BUTTON_8;
// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
// logicalId = JoystickButton.BUTTON_9;
// Select and start buttons
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_SELECT) {
logicalId = JoystickButton.BUTTON_8;
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_START) {
logicalId = JoystickButton.BUTTON_9;
// Joystick push buttons
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_THUMBL) {
logicalId = JoystickButton.BUTTON_10;
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_THUMBR) {
logicalId = JoystickButton.BUTTON_11;
}
JoystickButton button = new DefaultJoystickButton( getInputManager(), this, getButtonCount(),
name, logicalId );
addButton(button);
buttonIndex.put( keyCode, button );
return button;
}
protected JoystickAxis addAxis(MotionRange motionRange) {
String name = MotionEvent.axisToString(motionRange.getAxis());
String logicalId = MotionEvent.axisToString(motionRange.getAxis());
if (motionRange.getAxis() == MotionEvent.AXIS_X) {
logicalId = JoystickAxis.X_AXIS;
} else if (motionRange.getAxis() == MotionEvent.AXIS_Y) {
logicalId = JoystickAxis.Y_AXIS;
} else if (motionRange.getAxis() == MotionEvent.AXIS_Z) {
logicalId = JoystickAxis.Z_AXIS;
} else if (motionRange.getAxis() == MotionEvent.AXIS_RZ) {
logicalId = JoystickAxis.Z_ROTATION;
} else if (motionRange.getAxis() == MotionEvent.AXIS_HAT_X) {
logicalId = JoystickAxis.POV_X;
} else if (motionRange.getAxis() == MotionEvent.AXIS_HAT_Y) {
logicalId = JoystickAxis.POV_Y;
}
// String logicalId = JoystickCompatibilityMappings.remapComponent( controller.getName(), original );
// if( name != original ) {
// logger.log(Level.FINE, "Remapped:" + original + " to:" + logicalId);
// }
JoystickAxis axis = new DefaultJoystickAxis(getInputManager(),
this,
getAxisCount(),
name,
logicalId,
true,
true,
motionRange.getFlat());
if (motionRange.getAxis() == MotionEvent.AXIS_X) {
xAxis = axis;
}
if (motionRange.getAxis() == MotionEvent.AXIS_Y) {
yAxis = axis;
}
if (motionRange.getAxis() == MotionEvent.AXIS_HAT_X) {
povX = axis;
}
if (motionRange.getAxis() == MotionEvent.AXIS_HAT_Y) {
povY = axis;
}
addAxis(axis);
axisIndex.put(motionRange.getAxis(), axis);
return axis;
}
@Override
public JoystickAxis getXAxis() {
return xAxis;
}
@Override
public JoystickAxis getYAxis() {
return yAxis;
}
@Override
public JoystickAxis getPovXAxis() {
return povX;
}
@Override
public JoystickAxis getPovYAxis() {
return povY;
}
@Override
public int getXAxisIndex(){
return xAxis.getAxisId();
}
@Override
public int getYAxisIndex(){
return yAxis.getAxisId();
}
}
}

@ -1,140 +0,0 @@
/*
* 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.content.Context;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.input.event.TouchEvent;
import java.util.logging.Logger;
/**
* AndroidKeyHandler recieves onKey events from the Android system and creates
* the jME KeyEvents. onKey is used by Android to receive keys from the keyboard
* or device buttons. All key events are consumed by jME except for the Volume
* buttons and menu button.
*
* This class also provides the functionality to display or hide the soft keyboard
* for inputing single key events. Use OGLESContext to display an dialog to type
* in complete strings.
*
* @author iwgeric
*/
public class AndroidKeyHandler implements View.OnKeyListener {
private static final Logger logger = Logger.getLogger(AndroidKeyHandler.class.getName());
private AndroidInputHandler androidInput;
private boolean sendKeyEvents = true;
public AndroidKeyHandler(AndroidInputHandler androidInput) {
this.androidInput = androidInput;
}
public void initialize() {
}
public void destroy() {
}
public void setView(View view) {
if (view != null) {
view.setOnKeyListener(this);
} else {
androidInput.getView().setOnKeyListener(null);
}
}
/**
* onKey gets called from android thread on key events
*/
public boolean onKey(View view, int keyCode, KeyEvent event) {
if (androidInput.isInitialized() && view != androidInput.getView()) {
return false;
}
TouchEvent evt;
// TODO: get touch event from pool
if (event.getAction() == KeyEvent.ACTION_DOWN) {
evt = new TouchEvent();
evt.set(TouchEvent.Type.KEY_DOWN);
evt.setKeyCode(keyCode);
evt.setCharacters(event.getCharacters());
evt.setTime(event.getEventTime());
// Send the event
androidInput.addEvent(evt);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
evt = new TouchEvent();
evt.set(TouchEvent.Type.KEY_UP);
evt.setKeyCode(keyCode);
evt.setCharacters(event.getCharacters());
evt.setTime(event.getEventTime());
// Send the event
androidInput.addEvent(evt);
}
if (androidInput.isSimulateKeyboard()) {
KeyInputEvent kie;
char unicodeChar = (char)event.getUnicodeChar();
int jmeKeyCode = AndroidKeyMapping.getJmeKey(keyCode);
boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN;
boolean repeating = pressed && event.getRepeatCount() > 0;
kie = new KeyInputEvent(jmeKeyCode, unicodeChar, pressed, repeating);
kie.setTime(event.getEventTime());
androidInput.addEvent(kie);
// logger.log(Level.FINE, "onKey keyCode: {0}, jmeKeyCode: {1}, pressed: {2}, repeating: {3}",
// new Object[]{keyCode, jmeKeyCode, pressed, repeating});
// logger.log(Level.FINE, "creating KeyInputEvent: {0}", kie);
}
// consume all keys ourself except Volume Up/Down and Menu
// Don't do Menu so that typical Android Menus can be created and used
// by the user in MainActivity
if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) ||
(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) ||
(keyCode == KeyEvent.KEYCODE_MENU)) {
return false;
} else {
return true;
}
}
}

@ -37,13 +37,14 @@ import java.util.logging.Logger;
/**
* AndroidKeyMapping is just a utility to convert the Android keyCodes into
* jME KeyCodes received in jME's KeyEvent will match between Desktop and Android.
*
* jME KeyCodes so that events received in jME's KeyEvent will match between
* Desktop and Android.
*
* @author iwgeric
*/
public class AndroidKeyMapping {
private static final Logger logger = Logger.getLogger(AndroidKeyMapping.class.getName());
private static final int[] ANDROID_TO_JME = {
0x0, // unknown
0x0, // key code soft left
@ -141,9 +142,13 @@ public class AndroidKeyMapping {
0x0,//media fastforward
0x0,//mute
};
public static int getJmeKey(int androidKey) {
return ANDROID_TO_JME[androidKey];
if (androidKey > ANDROID_TO_JME.length) {
return androidKey;
} else {
return ANDROID_TO_JME[androidKey];
}
}
}

@ -75,15 +75,15 @@ import java.util.logging.Logger;
public class AndroidSensorJoyInput implements SensorEventListener {
private final static Logger logger = Logger.getLogger(AndroidSensorJoyInput.class.getName());
private AndroidJoyInputHandler joyHandler;
private AndroidJoyInput joyInput;
private SensorManager sensorManager = null;
private WindowManager windowManager = null;
private IntMap<SensorData> sensors = new IntMap<SensorData>();
private int lastRotation = 0;
private boolean loaded = false;
public AndroidSensorJoyInput(AndroidJoyInputHandler joyHandler) {
this.joyHandler = joyHandler;
public AndroidSensorJoyInput(AndroidJoyInput joyInput) {
this.joyInput = joyInput;
}
/**
@ -96,7 +96,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
int sensorAccuracy = -1;
float[] lastValues;
final Object valuesLock = new Object();
ArrayList<AndroidJoystickAxis> axes = new ArrayList<AndroidJoystickAxis>();
ArrayList<AndroidSensorJoystickAxis> axes = new ArrayList<AndroidSensorJoystickAxis>();
boolean enabled = false;
boolean haveData = false;
@ -306,7 +306,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
*/
private boolean updateOrientation() {
SensorData sensorData;
AndroidJoystickAxis axis;
AndroidSensorJoystickAxis axis;
final float[] curInclinationMat = new float[16];
final float[] curRotationMat = new float[16];
final float[] rotatedRotationMat = new float[16];
@ -374,7 +374,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
sensorData.haveData = true;
} else {
if (axis.isChanged()) {
joyHandler.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue()));
joyInput.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue()));
}
}
}
@ -401,10 +401,10 @@ public class AndroidSensorJoyInput implements SensorEventListener {
public Joystick loadJoystick(int joyId, InputManager inputManager) {
SensorData sensorData;
AndroidJoystickAxis axis;
AndroidSensorJoystickAxis axis;
AndroidJoystick joystick = new AndroidJoystick(inputManager,
joyHandler,
AndroidSensorJoystick joystick = new AndroidSensorJoystick(inputManager,
joyInput,
joyId,
"AndroidSensorsJoystick");
@ -522,15 +522,15 @@ public class AndroidSensorJoyInput implements SensorEventListener {
if (!loaded) {
return;
}
logger.log(Level.FINE, "onSensorChanged for {0}: accuracy: {1}, values: {2}",
new Object[]{se.sensor.getName(), se.accuracy, se.values});
// logger.log(Level.FINE, "onSensorChanged for {0}: accuracy: {1}, values: {2}",
// new Object[]{se.sensor.getName(), se.accuracy, se.values});
int sensorType = se.sensor.getType();
SensorData sensorData = sensors.get(sensorType);
if (sensorData != null) {
logger.log(Level.FINE, "sensorData name: {0}, enabled: {1}, unreliable: {2}",
new Object[]{sensorData.sensor.getName(), sensorData.enabled, sensorData.sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE});
// logger.log(Level.FINE, "sensorData name: {0}, enabled: {1}, unreliable: {2}",
// new Object[]{sensorData.sensor.getName(), sensorData.enabled, sensorData.sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE});
}
if (sensorData != null && sensorData.sensor.equals(se.sensor) && sensorData.enabled) {
@ -543,8 +543,8 @@ public class AndroidSensorJoyInput implements SensorEventListener {
}
}
if (sensorData != null && sensorData.axes.size() > 0) {
AndroidJoystickAxis axis;
if (sensorData.axes.size() > 0) {
AndroidSensorJoystickAxis axis;
for (int i=0; i<se.values.length; i++) {
axis = sensorData.axes.get(i);
if (axis != null) {
@ -554,8 +554,8 @@ public class AndroidSensorJoyInput implements SensorEventListener {
} else {
if (axis.isChanged()) {
JoyAxisEvent event = new JoyAxisEvent(axis, axis.getJoystickAxisValue());
logger.log(Level.INFO, "adding JoyAxisEvent: {0}", event);
joyHandler.addEvent(event);
// logger.log(Level.INFO, "adding JoyAxisEvent: {0}", event);
joyInput.addEvent(event);
// joyHandler.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue()));
}
}
@ -585,14 +585,14 @@ public class AndroidSensorJoyInput implements SensorEventListener {
// End of SensorEventListener methods
protected class AndroidJoystick extends AbstractJoystick {
protected class AndroidSensorJoystick extends AbstractJoystick {
private JoystickAxis nullAxis;
private JoystickAxis xAxis;
private JoystickAxis yAxis;
private JoystickAxis povX;
private JoystickAxis povY;
public AndroidJoystick( InputManager inputManager, JoyInput joyInput,
public AndroidSensorJoystick( InputManager inputManager, JoyInput joyInput,
int joyId, String name){
super( inputManager, joyInput, joyId, name );
@ -606,10 +606,10 @@ public class AndroidSensorJoyInput implements SensorEventListener {
}
protected AndroidJoystickAxis addAxis(String axisName, String logicalName, int axisNum, float maxRawValue) {
AndroidJoystickAxis axis;
protected AndroidSensorJoystickAxis addAxis(String axisName, String logicalName, int axisNum, float maxRawValue) {
AndroidSensorJoystickAxis axis;
axis = new AndroidJoystickAxis(
axis = new AndroidSensorJoystickAxis(
getInputManager(), // InputManager (InputManager)
this, // parent Joystick (Joystick)
axisNum, // Axis Index (int)
@ -654,7 +654,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
}
public class AndroidJoystickAxis extends DefaultJoystickAxis implements SensorJoystickAxis {
public class AndroidSensorJoystickAxis extends DefaultJoystickAxis implements SensorJoystickAxis {
float zeroRawValue = 0f;
float curRawValue = 0f;
float lastRawValue = 0f;
@ -662,7 +662,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
float maxRawValue = FastMath.HALF_PI;
boolean enabled = true;
public AndroidJoystickAxis(InputManager inputManager, Joystick parent,
public AndroidSensorJoystickAxis(InputManager inputManager, Joystick parent,
int axisIndex, String name, String logicalId,
boolean isAnalog, boolean isRelative, float deadZone,
float maxRawValue) {

@ -1,257 +0,0 @@
/*
* 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.MotionEvent;
import android.view.View;
import com.jme3.input.event.InputEvent;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.input.event.TouchEvent;
import static com.jme3.input.event.TouchEvent.Type.DOWN;
import static com.jme3.input.event.TouchEvent.Type.MOVE;
import static com.jme3.input.event.TouchEvent.Type.UP;
import com.jme3.math.Vector2f;
import java.util.HashMap;
import java.util.logging.Logger;
/**
* AndroidTouchHandler is the base class that receives touch inputs from the
* Android system and creates the TouchEvents for jME. This class is designed
* to handle the base touch events for Android rev 9 (Android 2.3). This is
* extended by other classes to add features that were introducted after
* Android rev 9.
*
* @author iwgeric
*/
public class AndroidTouchHandler implements View.OnTouchListener {
private static final Logger logger = Logger.getLogger(AndroidTouchHandler.class.getName());
final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();
protected int numPointers = 0;
protected AndroidInputHandler androidInput;
protected AndroidGestureHandler gestureHandler;
public AndroidTouchHandler(AndroidInputHandler androidInput, AndroidGestureHandler gestureHandler) {
this.androidInput = androidInput;
this.gestureHandler = gestureHandler;
}
public void initialize() {
}
public void destroy() {
setView(null);
}
public void setView(View view) {
if (view != null) {
view.setOnTouchListener(this);
} else {
androidInput.getView().setOnTouchListener(null);
}
}
protected int getPointerIndex(MotionEvent event) {
return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
}
protected int getPointerId(MotionEvent event) {
return event.getPointerId(getPointerIndex(event));
}
protected int getAction(MotionEvent event) {
return event.getAction() & MotionEvent.ACTION_MASK;
}
/**
* onTouch gets called from android thread on touch events
*/
public boolean onTouch(View view, MotionEvent event) {
if (!androidInput.isInitialized() || view != androidInput.getView()) {
return false;
}
boolean bWasHandled = false;
TouchEvent touch = null;
// System.out.println("native : " + event.getAction());
int action = getAction(event);
int pointerIndex = getPointerIndex(event);
int pointerId = getPointerId(event);
Vector2f lastPos = lastPositions.get(pointerId);
float jmeX;
float jmeY;
numPointers = event.getPointerCount();
// final int historySize = event.getHistorySize();
//final int pointerCount = event.getPointerCount();
switch (getAction(event)) {
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_DOWN:
jmeX = androidInput.getJmeX(event.getX(pointerIndex));
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex)));
touch = androidInput.getFreeTouchEvent();
touch.set(TouchEvent.Type.DOWN, jmeX, jmeY, 0, 0);
touch.setPointerId(pointerId);
touch.setTime(event.getEventTime());
touch.setPressure(event.getPressure(pointerIndex));
lastPos = new Vector2f(jmeX, jmeY);
lastPositions.put(pointerId, lastPos);
processEvent(touch);
bWasHandled = true;
break;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
jmeX = androidInput.getJmeX(event.getX(pointerIndex));
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex)));
touch = androidInput.getFreeTouchEvent();
touch.set(TouchEvent.Type.UP, jmeX, jmeY, 0, 0);
touch.setPointerId(pointerId);
touch.setTime(event.getEventTime());
touch.setPressure(event.getPressure(pointerIndex));
lastPositions.remove(pointerId);
processEvent(touch);
bWasHandled = true;
break;
case MotionEvent.ACTION_MOVE:
// Convert all pointers into events
for (int p = 0; p < event.getPointerCount(); p++) {
jmeX = androidInput.getJmeX(event.getX(p));
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(p)));
lastPos = lastPositions.get(event.getPointerId(p));
if (lastPos == null) {
lastPos = new Vector2f(jmeX, jmeY);
lastPositions.put(event.getPointerId(p), lastPos);
}
float dX = jmeX - lastPos.x;
float dY = jmeY - lastPos.y;
if (dX != 0 || dY != 0) {
touch = androidInput.getFreeTouchEvent();
touch.set(TouchEvent.Type.MOVE, jmeX, jmeY, dX, dY);
touch.setPointerId(event.getPointerId(p));
touch.setTime(event.getEventTime());
touch.setPressure(event.getPressure(p));
lastPos.set(jmeX, jmeY);
processEvent(touch);
bWasHandled = true;
}
}
break;
case MotionEvent.ACTION_OUTSIDE:
break;
}
// Try to detect gestures
if (gestureHandler != null) {
gestureHandler.detectGesture(event);
}
return bWasHandled;
}
protected void processEvent(TouchEvent event) {
// Add the touch event
androidInput.addEvent(event);
// MouseEvents do not support multi-touch, so only evaluate 1 finger pointer events
if (androidInput.isSimulateMouse() && numPointers == 1) {
InputEvent mouseEvent = generateMouseEvent(event);
if (mouseEvent != null) {
// Add the mouse event
androidInput.addEvent(mouseEvent);
}
}
}
// TODO: Ring Buffer for mouse events?
protected 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();
}
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 DOWN:
// Handle mouse down event
inputEvent = new MouseButtonEvent(0, true, newX, newY);
inputEvent.setTime(event.getTime());
break;
case UP:
// Handle mouse up event
inputEvent = new MouseButtonEvent(0, false, newX, newY);
inputEvent.setTime(event.getTime());
break;
case HOVER_MOVE:
case MOVE:
inputEvent = new MouseMotionEvent(newX, newY, newDX, newDY, (int)event.getScaleSpan(), (int)event.getDeltaScaleSpan());
inputEvent.setTime(event.getTime());
break;
}
return inputEvent;
}
}

@ -0,0 +1,475 @@
/*
* 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.KeyEvent;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import com.jme3.input.RawInputListener;
import com.jme3.input.TouchInput;
import com.jme3.input.event.InputEvent;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.input.event.TouchEvent;
import static com.jme3.input.event.TouchEvent.Type.DOWN;
import static com.jme3.input.event.TouchEvent.Type.MOVE;
import static com.jme3.input.event.TouchEvent.Type.UP;
import com.jme3.math.Vector2f;
import com.jme3.system.AppSettings;
import java.util.HashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* AndroidTouchInput is the base class that receives touch inputs from the
* Android system and creates the TouchEvents for jME. This class is designed
* to handle the base touch events for Android rev 9 (Android 2.3). This is
* extended by other classes to add features that were introducted after
* Android rev 9.
*
* @author iwgeric
*/
public class AndroidTouchInput implements TouchInput {
private static final Logger logger = Logger.getLogger(AndroidTouchInput.class.getName());
private boolean mouseEventsEnabled = true;
private boolean mouseEventsInvertX = false;
private boolean mouseEventsInvertY = false;
private boolean keyboardEventsEnabled = false;
private boolean dontSendHistory = false;
protected int numPointers = 0;
final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();
final private ConcurrentLinkedQueue<InputEvent> inputEventQueue = new ConcurrentLinkedQueue<InputEvent>();
private final static int MAX_TOUCH_EVENTS = 1024;
private final TouchEventPool touchEventPool = new TouchEventPool(MAX_TOUCH_EVENTS);
private float scaleX = 1f;
private float scaleY = 1f;
private boolean initialized = false;
private RawInputListener listener = null;
private GestureDetector gestureDetector;
private ScaleGestureDetector scaleDetector;
protected AndroidInputHandler androidInput;
public AndroidTouchInput(AndroidInputHandler androidInput) {
this.androidInput = androidInput;
}
public GestureDetector getGestureDetector() {
return gestureDetector;
}
public void setGestureDetector(GestureDetector gestureDetector) {
this.gestureDetector = gestureDetector;
}
public ScaleGestureDetector getScaleDetector() {
return scaleDetector;
}
public void setScaleDetector(ScaleGestureDetector scaleDetector) {
this.scaleDetector = scaleDetector;
}
public float invertX(float origX) {
return getJmeX(androidInput.getView().getWidth()) - origX;
}
public float invertY(float origY) {
return getJmeY(androidInput.getView().getHeight()) - origY;
}
public float getJmeX(float origX) {
return origX * scaleX;
}
public float getJmeY(float origY) {
return origY * scaleY;
}
public void loadSettings(AppSettings settings) {
keyboardEventsEnabled = settings.isEmulateKeyboard();
mouseEventsEnabled = settings.isEmulateMouse();
mouseEventsInvertX = settings.isEmulateMouseFlipX();
mouseEventsInvertY = settings.isEmulateMouseFlipY();
// view width and height are 0 until the view is displayed on the screen
if (androidInput.getView().getWidth() != 0 && androidInput.getView().getHeight() != 0) {
scaleX = (float)settings.getWidth() / (float)androidInput.getView().getWidth();
scaleY = (float)settings.getHeight() / (float)androidInput.getView().getHeight();
}
logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}",
new Object[]{scaleX, scaleY});
}
protected int getPointerIndex(MotionEvent event) {
return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
}
protected int getPointerId(MotionEvent event) {
return event.getPointerId(getPointerIndex(event));
}
protected int getAction(MotionEvent event) {
return event.getAction() & MotionEvent.ACTION_MASK;
}
public boolean onTouch(MotionEvent event) {
if (!isInitialized()) {
return false;
}
boolean bWasHandled = false;
TouchEvent touch = null;
// System.out.println("native : " + event.getAction());
int action = getAction(event);
int pointerIndex = getPointerIndex(event);
int pointerId = getPointerId(event);
Vector2f lastPos = lastPositions.get(pointerId);
float jmeX;
float jmeY;
numPointers = event.getPointerCount();
// final int historySize = event.getHistorySize();
//final int pointerCount = event.getPointerCount();
switch (getAction(event)) {
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_DOWN:
jmeX = getJmeX(event.getX(pointerIndex));
jmeY = invertY(getJmeY(event.getY(pointerIndex)));
touch = getFreeTouchEvent();
touch.set(TouchEvent.Type.DOWN, jmeX, jmeY, 0, 0);
touch.setPointerId(pointerId);
touch.setTime(event.getEventTime());
touch.setPressure(event.getPressure(pointerIndex));
lastPos = new Vector2f(jmeX, jmeY);
lastPositions.put(pointerId, lastPos);
addEvent(touch);
addEvent(generateMouseEvent(touch));
bWasHandled = true;
break;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
jmeX = getJmeX(event.getX(pointerIndex));
jmeY = invertY(getJmeY(event.getY(pointerIndex)));
touch = getFreeTouchEvent();
touch.set(TouchEvent.Type.UP, jmeX, jmeY, 0, 0);
touch.setPointerId(pointerId);
touch.setTime(event.getEventTime());
touch.setPressure(event.getPressure(pointerIndex));
lastPositions.remove(pointerId);
addEvent(touch);
addEvent(generateMouseEvent(touch));
bWasHandled = true;
break;
case MotionEvent.ACTION_MOVE:
// Convert all pointers into events
for (int p = 0; p < event.getPointerCount(); p++) {
jmeX = getJmeX(event.getX(p));
jmeY = invertY(getJmeY(event.getY(p)));
lastPos = lastPositions.get(event.getPointerId(p));
if (lastPos == null) {
lastPos = new Vector2f(jmeX, jmeY);
lastPositions.put(event.getPointerId(p), lastPos);
}
float dX = jmeX - lastPos.x;
float dY = jmeY - lastPos.y;
if (dX != 0 || dY != 0) {
touch = getFreeTouchEvent();
touch.set(TouchEvent.Type.MOVE, jmeX, jmeY, dX, dY);
touch.setPointerId(event.getPointerId(p));
touch.setTime(event.getEventTime());
touch.setPressure(event.getPressure(p));
lastPos.set(jmeX, jmeY);
addEvent(touch);
addEvent(generateMouseEvent(touch));
bWasHandled = true;
}
}
break;
case MotionEvent.ACTION_OUTSIDE:
break;
}
// Try to detect gestures
if (gestureDetector != null) {
gestureDetector.onTouchEvent(event);
}
if (scaleDetector != null) {
scaleDetector.onTouchEvent(event);
}
return bWasHandled;
}
// TODO: Ring Buffer for mouse events?
public InputEvent generateMouseEvent(TouchEvent event) {
InputEvent inputEvent = null;
int newX;
int newY;
int newDX;
int newDY;
// MouseEvents do not support multi-touch, so only evaluate 1 finger pointer events
if (!isSimulateMouse() || numPointers > 1) {
return null;
}
if (isMouseEventsInvertX()) {
newX = (int) (invertX(event.getX()));
newDX = (int)event.getDeltaX() * -1;
} else {
newX = (int) event.getX();
newDX = (int)event.getDeltaX();
}
if (isMouseEventsInvertY()) {
newY = (int) (invertY(event.getY()));
newDY = (int)event.getDeltaY() * -1;
} else {
newY = (int) event.getY();
newDY = (int)event.getDeltaY();
}
switch (event.getType()) {
case DOWN:
// Handle mouse down event
inputEvent = new MouseButtonEvent(0, true, newX, newY);
inputEvent.setTime(event.getTime());
break;
case UP:
// Handle mouse up event
inputEvent = new MouseButtonEvent(0, false, newX, newY);
inputEvent.setTime(event.getTime());
break;
case HOVER_MOVE:
case MOVE:
inputEvent = new MouseMotionEvent(newX, newY, newDX, newDY, (int)event.getScaleSpan(), (int)event.getDeltaScaleSpan());
inputEvent.setTime(event.getTime());
break;
}
return inputEvent;
}
public boolean onKey(KeyEvent event) {
if (!isInitialized()) {
return false;
}
TouchEvent evt;
// TODO: get touch event from pool
if (event.getAction() == KeyEvent.ACTION_DOWN) {
evt = new TouchEvent();
evt.set(TouchEvent.Type.KEY_DOWN);
evt.setKeyCode(event.getKeyCode());
evt.setCharacters(event.getCharacters());
evt.setTime(event.getEventTime());
// Send the event
addEvent(evt);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
evt = new TouchEvent();
evt.set(TouchEvent.Type.KEY_UP);
evt.setKeyCode(event.getKeyCode());
evt.setCharacters(event.getCharacters());
evt.setTime(event.getEventTime());
// Send the event
addEvent(evt);
}
if (isSimulateKeyboard()) {
KeyInputEvent kie;
char unicodeChar = (char)event.getUnicodeChar();
int jmeKeyCode = AndroidKeyMapping.getJmeKey(event.getKeyCode());
boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN;
boolean repeating = pressed && event.getRepeatCount() > 0;
kie = new KeyInputEvent(jmeKeyCode, unicodeChar, pressed, repeating);
kie.setTime(event.getEventTime());
addEvent(kie);
// logger.log(Level.FINE, "onKey keyCode: {0}, jmeKeyCode: {1}, pressed: {2}, repeating: {3}",
// new Object[]{event.getKeyCode(), jmeKeyCode, pressed, repeating});
// logger.log(Level.FINE, "creating KeyInputEvent: {0}", kie);
}
// consume all keys ourself except Volume Up/Down and Menu
// Don't do Menu so that typical Android Menus can be created and used
// by the user in MainActivity
if ((event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) ||
(event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) ||
(event.getKeyCode() == KeyEvent.KEYCODE_MENU)) {
return false;
} else {
return true;
}
}
// -----------------------------------------
// JME3 Input interface
@Override
public void initialize() {
touchEventPool.initialize();
initialized = true;
}
@Override
public void destroy() {
initialized = false;
touchEventPool.destroy();
}
@Override
public boolean isInitialized() {
return initialized;
}
@Override
public void setInputListener(RawInputListener listener) {
this.listener = listener;
}
@Override
public long getInputTimeNanos() {
return System.nanoTime();
}
@Override
public void update() {
if (listener != null) {
InputEvent inputEvent;
while ((inputEvent = inputEventQueue.poll()) != null) {
if (inputEvent instanceof TouchEvent) {
listener.onTouchEvent((TouchEvent)inputEvent);
} else if (inputEvent instanceof MouseButtonEvent) {
listener.onMouseButtonEvent((MouseButtonEvent)inputEvent);
} else if (inputEvent instanceof MouseMotionEvent) {
listener.onMouseMotionEvent((MouseMotionEvent)inputEvent);
} else if (inputEvent instanceof KeyInputEvent) {
listener.onKeyEvent((KeyInputEvent)inputEvent);
}
}
}
}
// -----------------------------------------
public TouchEvent getFreeTouchEvent() {
return touchEventPool.getNextFreeEvent();
}
public void addEvent(InputEvent event) {
if (event == null) {
return;
}
logger.log(Level.INFO, "event: {0}", event);
inputEventQueue.add(event);
if (event instanceof TouchEvent) {
touchEventPool.storeEvent((TouchEvent)event);
}
}
@Override
public void setSimulateMouse(boolean simulate) {
this.mouseEventsEnabled = simulate;
}
@Override
public boolean isSimulateMouse() {
return mouseEventsEnabled;
}
public boolean isMouseEventsInvertX() {
return mouseEventsInvertX;
}
public boolean isMouseEventsInvertY() {
return mouseEventsInvertY;
}
@Override
public void setSimulateKeyboard(boolean simulate) {
this.keyboardEventsEnabled = simulate;
}
@Override
public boolean isSimulateKeyboard() {
return keyboardEventsEnabled;
}
@Override
public void setOmitHistoricEvents(boolean dontSendHistory) {
this.dontSendHistory = dontSendHistory;
}
}

@ -33,7 +33,6 @@
package com.jme3.input.android;
import android.view.MotionEvent;
import android.view.View;
import com.jme3.input.event.TouchEvent;
import com.jme3.math.Vector2f;
import java.util.HashMap;
@ -41,36 +40,20 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
* AndroidTouchHandler14 is an extension of AndroidTouchHander that adds the
* Android touch event functionality between Android rev 9 (Android 2.3) and
* Android rev 14 (Android 4.0).
*
* AndroidTouchHandler14 extends AndroidTouchHandler to process the onHover
* events added in Android rev 14 (Android 4.0).
*
* @author iwgeric
*/
public class AndroidTouchHandler14 extends AndroidTouchHandler implements
View.OnHoverListener {
private static final Logger logger = Logger.getLogger(AndroidTouchHandler14.class.getName());
public class AndroidTouchInput14 extends AndroidTouchInput {
private static final Logger logger = Logger.getLogger(AndroidTouchInput14.class.getName());
final private HashMap<Integer, Vector2f> lastHoverPositions = new HashMap<Integer, Vector2f>();
public AndroidTouchHandler14(AndroidInputHandler androidInput, AndroidGestureHandler gestureHandler) {
super(androidInput, gestureHandler);
}
@Override
public void setView(View view) {
if (view != null) {
view.setOnHoverListener(this);
} else {
androidInput.getView().setOnHoverListener(null);
}
super.setView(view);
public AndroidTouchInput14(AndroidInputHandler androidInput) {
super(androidInput);
}
public boolean onHover(View view, MotionEvent event) {
if (view == null || view != androidInput.getView()) {
return false;
}
public boolean onHover(MotionEvent event) {
boolean consumed = false;
int action = getAction(event);
int pointerId = getPointerId(event);
@ -78,34 +61,34 @@ public class AndroidTouchHandler14 extends AndroidTouchHandler implements
Vector2f lastPos = lastHoverPositions.get(pointerId);
float jmeX;
float jmeY;
numPointers = event.getPointerCount();
logger.log(Level.INFO, "onHover pointerId: {0}, action: {1}, x: {2}, y: {3}, numPointers: {4}",
new Object[]{pointerId, action, event.getX(), event.getY(), event.getPointerCount()});
// logger.log(Level.INFO, "onHover pointerId: {0}, action: {1}, x: {2}, y: {3}, numPointers: {4}",
// new Object[]{pointerId, action, event.getX(), event.getY(), event.getPointerCount()});
TouchEvent touchEvent;
switch (action) {
case MotionEvent.ACTION_HOVER_ENTER:
jmeX = androidInput.getJmeX(event.getX(pointerIndex));
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex)));
touchEvent = androidInput.getFreeTouchEvent();
jmeX = getJmeX(event.getX(pointerIndex));
jmeY = invertY(getJmeY(event.getY(pointerIndex)));
touchEvent = getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.HOVER_START, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(pointerId);
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure(pointerIndex));
lastPos = new Vector2f(jmeX, jmeY);
lastHoverPositions.put(pointerId, lastPos);
processEvent(touchEvent);
addEvent(touchEvent);
consumed = true;
break;
case MotionEvent.ACTION_HOVER_MOVE:
// Convert all pointers into events
for (int p = 0; p < event.getPointerCount(); p++) {
jmeX = androidInput.getJmeX(event.getX(p));
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(p)));
jmeX = getJmeX(event.getX(p));
jmeY = invertY(getJmeY(event.getY(p)));
lastPos = lastHoverPositions.get(event.getPointerId(p));
if (lastPos == null) {
lastPos = new Vector2f(jmeX, jmeY);
@ -115,38 +98,39 @@ public class AndroidTouchHandler14 extends AndroidTouchHandler implements
float dX = jmeX - lastPos.x;
float dY = jmeY - lastPos.y;
if (dX != 0 || dY != 0) {
touchEvent = androidInput.getFreeTouchEvent();
touchEvent = getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.HOVER_MOVE, jmeX, jmeY, dX, dY);
touchEvent.setPointerId(event.getPointerId(p));
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure(p));
lastPos.set(jmeX, jmeY);
processEvent(touchEvent);
addEvent(touchEvent);
}
}
consumed = true;
break;
case MotionEvent.ACTION_HOVER_EXIT:
jmeX = androidInput.getJmeX(event.getX(pointerIndex));
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex)));
touchEvent = androidInput.getFreeTouchEvent();
jmeX = getJmeX(event.getX(pointerIndex));
jmeY = invertY(getJmeY(event.getY(pointerIndex)));
touchEvent = getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.HOVER_END, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(pointerId);
touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure(pointerIndex));
lastHoverPositions.remove(pointerId);
processEvent(touchEvent);
addEvent(touchEvent);
consumed = true;
break;
default:
consumed = false;
break;
}
return consumed;
}
}

@ -47,7 +47,7 @@ import android.widget.EditText;
import android.widget.FrameLayout;
import com.jme3.input.*;
import com.jme3.input.android.AndroidInputHandler;
import com.jme3.input.android.AndroidJoyInputHandler;
import com.jme3.input.android.AndroidInputHandler14;
import com.jme3.input.controls.SoftTextDialogInputListener;
import com.jme3.input.dummy.DummyKeyInput;
import com.jme3.input.dummy.DummyMouseInput;
@ -75,7 +75,6 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
protected SystemListener listener;
protected boolean autoFlush = true;
protected AndroidInputHandler androidInput;
protected AndroidJoyInputHandler androidJoyInput = null;
protected long minFrameDuration = 0; // No FPS cap
protected long lastUpdateTime = 0;
@ -111,18 +110,17 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
// Start to set up the view
GLSurfaceView view = new GLSurfaceView(context);
logger.log(Level.INFO, "Android Build Version: {0}", Build.VERSION.SDK_INT);
if (androidInput == null) {
androidInput = new AndroidInputHandler();
if (Build.VERSION.SDK_INT >= 14) {
androidInput = new AndroidInputHandler14();
} else if (Build.VERSION.SDK_INT >= 9){
androidInput = new AndroidInputHandler();
}
}
androidInput.setView(view);
androidInput.loadSettings(settings);
if (androidJoyInput == null) {
androidJoyInput = new AndroidJoyInputHandler();
}
androidJoyInput.setView(view);
androidJoyInput.loadSettings(settings);
// setEGLContextClientVersion must be set before calling setRenderer
// this means it cannot be set in AndroidConfigChooser (too late)
view.setEGLContextClientVersion(2);
@ -235,9 +233,6 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
if (androidInput != null) {
androidInput.loadSettings(settings);
}
if (androidJoyInput != null) {
androidJoyInput.loadSettings(settings);
}
if (settings.getFrameRate() > 0) {
minFrameDuration = (long)(1000d / (double)settings.getFrameRate()); // ms
@ -274,12 +269,12 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
@Override
public JoyInput getJoyInput() {
return androidJoyInput;
return androidInput.getJoyInput();
}
@Override
public TouchInput getTouchInput() {
return androidInput;
return androidInput.getTouchInput();
}
@Override

Loading…
Cancel
Save