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.GestureDetector;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.ScaleGestureDetector; 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 com.jme3.input.event.TouchEvent;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
* AndroidGestureHandler uses Gesture type listeners to create jME TouchEvents * 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 * on Android rev 9 (Android 2.3). Extend this class to add functionality
* added by Android after rev 9. * added by Android after rev 9.
* *
* @author iwgeric * @author iwgeric
*/ */
public class AndroidGestureHandler implements public class AndroidGestureProcessor implements
GestureDetector.OnGestureListener, GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener, GestureDetector.OnDoubleTapListener,
ScaleGestureDetector.OnScaleGestureListener { ScaleGestureDetector.OnScaleGestureListener {
private static final Logger logger = Logger.getLogger(AndroidGestureHandler.class.getName()); private static final Logger logger = Logger.getLogger(AndroidGestureProcessor.class.getName());
private AndroidInputHandler androidInput;
private GestureDetector gestureDetector; private AndroidTouchInput touchInput;
private ScaleGestureDetector scaleDetector;
float gestureDownX = -1f; float gestureDownX = -1f;
float gestureDownY = -1f; float gestureDownY = -1f;
float scaleStartX = -1f; float scaleStartX = -1f;
float scaleStartY = -1f; float scaleStartY = -1f;
public AndroidGestureHandler(AndroidInputHandler androidInput) { public AndroidGestureProcessor(AndroidTouchInput touchInput) {
this.androidInput = androidInput; this.touchInput = touchInput;
}
public void initialize() {
}
public void destroy() {
setView(null);
}
public void setView(View view) {
if (view != null) {
gestureDetector = new GestureDetector(view.getContext(), this);
scaleDetector = new ScaleGestureDetector(view.getContext(), this);
} else {
gestureDetector = null;
scaleDetector = null;
}
}
public void detectGesture(MotionEvent event) {
if (gestureDetector != null && scaleDetector != null) {
gestureDetector.onTouchEvent(event);
scaleDetector.onTouchEvent(event);
}
}
private int getPointerIndex(MotionEvent event) {
return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
}
private int getPointerId(MotionEvent event) {
return event.getPointerId(getPointerIndex(event));
} }
private void processEvent(TouchEvent event) {
// Add the touch event
androidInput.addEvent(event);
if (androidInput.isSimulateMouse()) {
InputEvent mouseEvent = generateMouseEvent(event);
if (mouseEvent != null) {
// Add the mouse event
androidInput.addEvent(mouseEvent);
}
}
}
// TODO: Ring Buffer for mouse events?
private InputEvent generateMouseEvent(TouchEvent event) {
InputEvent inputEvent = null;
int newX;
int newY;
int newDX;
int newDY;
if (androidInput.isMouseEventsInvertX()) {
newX = (int) (androidInput.invertX(event.getX()));
newDX = (int)event.getDeltaX() * -1;
} else {
newX = (int) event.getX();
newDX = (int)event.getDeltaX();
}
int wheel = (int) (event.getScaleSpan()); // might need to scale to match mouse wheel
int dWheel = (int) (event.getDeltaScaleSpan()); // might need to scale to match mouse wheel
if (androidInput.isMouseEventsInvertY()) {
newY = (int) (androidInput.invertY(event.getY()));
newDY = (int)event.getDeltaY() * -1;
} else {
newY = (int) event.getY();
newDY = (int)event.getDeltaY();
}
switch (event.getType()) {
case SCALE_MOVE:
inputEvent = new MouseMotionEvent(newX, newY, newDX, newDY, wheel, dWheel);
inputEvent.setTime(event.getTime());
break;
}
return inputEvent;
}
/* Events from onGestureListener */ /* Events from onGestureListener */
@Override
public boolean onDown(MotionEvent event) { public boolean onDown(MotionEvent event) {
// start of all GestureListeners. Not really a gesture by itself // start of all GestureListeners. Not really a gesture by itself
// so we don't create an event. // so we don't create an event.
// However, reset the scaleInProgress here since this is the beginning // However, reset the scaleInProgress here since this is the beginning
// of a series of gesture events. // of a series of gesture events.
// logger.log(Level.INFO, "onDown pointerId: {0}, action: {1}, x: {2}, y: {3}", // logger.log(Level.INFO, "onDown pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()}); // new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
gestureDownX = androidInput.getJmeX(event.getX()); gestureDownX = touchInput.getJmeX(event.getX());
gestureDownY = androidInput.invertY(androidInput.getJmeY(event.getY())); gestureDownY = touchInput.invertY(touchInput.getJmeY(event.getY()));
return true; return true;
} }
@Override
public boolean onSingleTapUp(MotionEvent event) { public boolean onSingleTapUp(MotionEvent event) {
// Up of single tap. May be followed by a double tap later. // Up of single tap. May be followed by a double tap later.
// use onSingleTapConfirmed instead. // use onSingleTapConfirmed instead.
// logger.log(Level.INFO, "onSingleTapUp pointerId: {0}, action: {1}, x: {2}, y: {3}", // logger.log(Level.INFO, "onSingleTapUp pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()}); // new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
return true; return true;
} }
@Override
public void onShowPress(MotionEvent event) { public void onShowPress(MotionEvent event) {
// logger.log(Level.INFO, "onShowPress pointerId: {0}, action: {1}, x: {2}, y: {3}", // logger.log(Level.INFO, "onShowPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()}); // new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
float jmeX = androidInput.getJmeX(event.getX()); float jmeX = touchInput.getJmeX(event.getX());
float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY())); float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
TouchEvent touchEvent = androidInput.getFreeTouchEvent(); TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SHOWPRESS, jmeX, jmeY, 0, 0); touchEvent.set(TouchEvent.Type.SHOWPRESS, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(getPointerId(event)); touchEvent.setPointerId(touchInput.getPointerId(event));
touchEvent.setTime(event.getEventTime()); touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure()); touchEvent.setPressure(event.getPressure());
processEvent(touchEvent); touchInput.addEvent(touchEvent);
} }
@Override
public void onLongPress(MotionEvent event) { public void onLongPress(MotionEvent event) {
// logger.log(Level.INFO, "onLongPress pointerId: {0}, action: {1}, x: {2}, y: {3}", // logger.log(Level.INFO, "onLongPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()}); // new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
float jmeX = androidInput.getJmeX(event.getX()); float jmeX = touchInput.getJmeX(event.getX());
float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY())); float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
TouchEvent touchEvent = androidInput.getFreeTouchEvent(); TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.LONGPRESSED, jmeX, jmeY, 0, 0); touchEvent.set(TouchEvent.Type.LONGPRESSED, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(getPointerId(event)); touchEvent.setPointerId(touchInput.getPointerId(event));
touchEvent.setTime(event.getEventTime()); touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure()); touchEvent.setPressure(event.getPressure());
processEvent(touchEvent); touchInput.addEvent(touchEvent);
} }
@Override
public boolean onScroll(MotionEvent startEvent, MotionEvent endEvent, float distX, float distY) { public boolean onScroll(MotionEvent startEvent, MotionEvent endEvent, float distX, float distY) {
// if not scaleInProgess, send scroll events. This is to avoid sending // 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. // scroll events when one of the fingers is lifted just before the other one.
// Avoids sending the scroll for that brief period of time. // 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. // 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 // 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. // for the fact that jME has y=0 at bottom where Android has y=0 at top.
// if (!scaleInProgress) { if (!touchInput.getScaleDetector().isInProgress()) {
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}",
// 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});
// new Object[]{getPointerId(startEvent), getAction(startEvent), startEvent.getX(), startEvent.getY(), getAction(endEvent), endEvent.getX(), endEvent.getY(), distX, distY});
float jmeX = androidInput.getJmeX(endEvent.getX()); float jmeX = touchInput.getJmeX(endEvent.getX());
float jmeY = androidInput.invertY(androidInput.getJmeY(endEvent.getY())); float jmeY = touchInput.invertY(touchInput.getJmeY(endEvent.getY()));
TouchEvent touchEvent = androidInput.getFreeTouchEvent(); TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SCROLL, jmeX, jmeY, androidInput.getJmeX(-distX), androidInput.getJmeY(distY)); touchEvent.set(TouchEvent.Type.SCROLL, jmeX, jmeY, touchInput.getJmeX(-distX), touchInput.getJmeY(distY));
touchEvent.setPointerId(getPointerId(endEvent)); touchEvent.setPointerId(touchInput.getPointerId(endEvent));
touchEvent.setTime(endEvent.getEventTime()); touchEvent.setTime(endEvent.getEventTime());
touchEvent.setPressure(endEvent.getPressure()); touchEvent.setPressure(endEvent.getPressure());
processEvent(touchEvent); touchInput.addEvent(touchEvent);
} }
return true; return true;
} }
@Override
public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float velocityX, float velocityY) { 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 happens only once at the end of the gesture (all fingers up).
// Fling returns the velocity of the finger movement in pixels/sec. // Fling returns the velocity of the finger movement in pixels/sec.
// Therefore, the dX and dY values are actually velocity instead of distance values // 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. // 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()); // logger.log(Level.INFO, "onFling pointerId: {0}, startAction: {1}, startX: {2}, startY: {3}, endAction: {4}, endX: {5}, endY: {6}, velocityX: {7}, velocityY: {8}",
float jmeY = androidInput.invertY(androidInput.getJmeY(startEvent.getY())); // new Object[]{touchInput.getPointerId(startEvent), touchInput.getAction(startEvent), startEvent.getX(), startEvent.getY(), touchInput.getAction(endEvent), endEvent.getX(), endEvent.getY(), velocityX, velocityY});
TouchEvent touchEvent = androidInput.getFreeTouchEvent();
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.set(TouchEvent.Type.FLING, jmeX, jmeY, velocityX, velocityY);
touchEvent.setPointerId(getPointerId(endEvent)); touchEvent.setPointerId(touchInput.getPointerId(endEvent));
touchEvent.setTime(endEvent.getEventTime()); touchEvent.setTime(endEvent.getEventTime());
touchEvent.setPressure(endEvent.getPressure()); touchEvent.setPressure(endEvent.getPressure());
processEvent(touchEvent); touchInput.addEvent(touchEvent);
return true; return true;
} }
/* Events from onDoubleTapListener */ /* Events from onDoubleTapListener */
@Override
public boolean onSingleTapConfirmed(MotionEvent event) { public boolean onSingleTapConfirmed(MotionEvent event) {
// Up of single tap when no double tap followed. // Up of single tap when no double tap followed.
// logger.log(Level.INFO, "onSingleTapConfirmed pointerId: {0}, action: {1}, x: {2}, y: {3}", // logger.log(Level.INFO, "onSingleTapConfirmed pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()}); // new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
float jmeX = androidInput.getJmeX(event.getX()); float jmeX = touchInput.getJmeX(event.getX());
float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY())); float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
TouchEvent touchEvent = androidInput.getFreeTouchEvent(); TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.TAP, jmeX, jmeY, 0, 0); touchEvent.set(TouchEvent.Type.TAP, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(getPointerId(event)); touchEvent.setPointerId(touchInput.getPointerId(event));
touchEvent.setTime(event.getEventTime()); touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure()); touchEvent.setPressure(event.getPressure());
processEvent(touchEvent); touchInput.addEvent(touchEvent);
return true; return true;
} }
@Override
public boolean onDoubleTap(MotionEvent event) { public boolean onDoubleTap(MotionEvent event) {
//The down motion event of the first tap of the double-tap //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 // DoubleTapEvent with a check for the UP action
// logger.log(Level.INFO, "onDoubleTap pointerId: {0}, action: {1}, x: {2}, y: {3}", // logger.log(Level.INFO, "onDoubleTap pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()}); // new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
float jmeX = androidInput.getJmeX(event.getX()); float jmeX = touchInput.getJmeX(event.getX());
float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY())); float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
TouchEvent touchEvent = androidInput.getFreeTouchEvent(); TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.DOUBLETAP, jmeX, jmeY, 0, 0); touchEvent.set(TouchEvent.Type.DOUBLETAP, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(getPointerId(event)); touchEvent.setPointerId(touchInput.getPointerId(event));
touchEvent.setTime(event.getEventTime()); touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure()); touchEvent.setPressure(event.getPressure());
processEvent(touchEvent); touchInput.addEvent(touchEvent);
return true; return true;
} }
@Override
public boolean onDoubleTapEvent(MotionEvent event) { public boolean onDoubleTapEvent(MotionEvent event) {
//Notified when an event within a double-tap gesture occurs, including the down, move(s), and up events. //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 // 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}", // logger.log(Level.INFO, "onDoubleTapEvent pointerId: {0}, action: {1}, x: {2}, y: {3}",
// new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()}); // new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
// if (getAction(event) == MotionEvent.ACTION_UP) { if (touchInput.getAction(event) == MotionEvent.ACTION_UP) {
// TouchEvent touchEvent = touchEventPool.getNextFreeEvent(); TouchEvent touchEvent = touchInput.getFreeTouchEvent();
// touchEvent.set(TouchEvent.Type.DOUBLETAP, event.getX(), androidInput.invertY(event.getY()), 0, 0); touchEvent.set(TouchEvent.Type.DOUBLETAP, event.getX(), touchInput.invertY(event.getY()), 0, 0);
// touchEvent.setPointerId(getPointerId(event)); touchEvent.setPointerId(touchInput.getPointerId(event));
// touchEvent.setTime(event.getEventTime()); touchEvent.setTime(event.getEventTime());
// touchEvent.setPressure(event.getPressure()); touchEvent.setPressure(event.getPressure());
// processEvent(touchEvent); touchInput.addEvent(touchEvent);
// } }
return true; return true;
} }
/* Events from ScaleGestureDetector */ /* Events from ScaleGestureDetector */
@Override
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) { public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
// Scale uses a focusX and focusY instead of x and y. Focus is the middle // 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 // 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. // 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 // return true or all gestures for this beginning event will be discarded
logger.log(Level.INFO, "onScaleBegin"); // logger.log(Level.INFO, "onScaleBegin");
scaleStartX = gestureDownX; scaleStartX = gestureDownX;
scaleStartY = gestureDownY; scaleStartY = gestureDownY;
TouchEvent touchEvent = androidInput.getFreeTouchEvent(); TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SCALE_START, scaleStartX, scaleStartY, 0f, 0f); touchEvent.set(TouchEvent.Type.SCALE_START, scaleStartX, scaleStartY, 0f, 0f);
touchEvent.setPointerId(0); touchEvent.setPointerId(0);
touchEvent.setTime(scaleGestureDetector.getEventTime()); touchEvent.setTime(scaleGestureDetector.getEventTime());
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan()); touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
touchEvent.setDeltaScaleSpan(0f); touchEvent.setDeltaScaleSpan(0f);
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor()); touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress()); touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
processEvent(touchEvent); touchInput.addEvent(touchEvent);
return true; return true;
} }
@Override
public boolean onScale(ScaleGestureDetector scaleGestureDetector) { public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
// return true or all gestures for this event will be accumulated // return true or all gestures for this event will be accumulated
logger.log(Level.INFO, "onScale"); // logger.log(Level.INFO, "onScale");
scaleStartX = gestureDownX; scaleStartX = gestureDownX;
scaleStartY = gestureDownY; scaleStartY = gestureDownY;
TouchEvent touchEvent = androidInput.getFreeTouchEvent(); TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SCALE_MOVE, scaleStartX, scaleStartY, 0f, 0f); touchEvent.set(TouchEvent.Type.SCALE_MOVE, scaleStartX, scaleStartY, 0f, 0f);
touchEvent.setPointerId(0); touchEvent.setPointerId(0);
touchEvent.setTime(scaleGestureDetector.getEventTime()); touchEvent.setTime(scaleGestureDetector.getEventTime());
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan()); touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan()); touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor()); touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress()); touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
processEvent(touchEvent); touchInput.addEvent(touchEvent);
return true; return true;
} }
@Override
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) { public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
logger.log(Level.INFO, "onScaleEnd"); // logger.log(Level.INFO, "onScaleEnd");
scaleStartX = gestureDownX; scaleStartX = gestureDownX;
scaleStartY = gestureDownY; scaleStartY = gestureDownY;
TouchEvent touchEvent = androidInput.getFreeTouchEvent(); TouchEvent touchEvent = touchInput.getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.SCALE_END, scaleStartX, scaleStartY, 0f, 0f); touchEvent.set(TouchEvent.Type.SCALE_END, scaleStartX, scaleStartY, 0f, 0f);
touchEvent.setPointerId(0); touchEvent.setPointerId(0);
touchEvent.setTime(scaleGestureDetector.getEventTime()); touchEvent.setTime(scaleGestureDetector.getEventTime());
touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan()); touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan()); touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor()); touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress()); touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
processEvent(touchEvent); 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; package com.jme3.input.android;
import android.opengl.GLSurfaceView; 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 android.view.View;
import com.jme3.input.RawInputListener; import com.jme3.input.JoyInput;
import com.jme3.input.TouchInput; 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 com.jme3.system.AppSettings;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
* <code>AndroidInput</code> is the main class that connects the Android system * <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 * inputs to jME. It receives the inputs from the Android View and passes them
* Android input methods and provides them to jME's <code>InputManager</code>. * 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 * @author iwgeric
*/ */
public class AndroidInputHandler implements TouchInput { public class AndroidInputHandler implements View.OnTouchListener,
private static final Logger logger = Logger.getLogger(AndroidInputHandler.class.getName()); View.OnKeyListener {
// Custom settings
private boolean mouseEventsEnabled = true;
private boolean mouseEventsInvertX = false;
private boolean mouseEventsInvertY = false;
private boolean keyboardEventsEnabled = false;
private boolean dontSendHistory = false;
private static final Logger logger = Logger.getLogger(AndroidInputHandler.class.getName());
// Internal protected GLSurfaceView view;
private GLSurfaceView view; protected AndroidTouchInput touchInput;
private AndroidTouchHandler touchHandler; protected AndroidJoyInput joyInput;
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;
public AndroidInputHandler() { public AndroidInputHandler() {
int buildVersion = Build.VERSION.SDK_INT; touchInput = new AndroidTouchInput(this);
logger.log(Level.INFO, "Android Build Version: {0}", buildVersion); joyInput = new AndroidJoyInput(this);
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;
} }
public void setView(View view) { public void setView(View view) {
if (touchHandler != null) { if (this.view != null && view != null && this.view.equals(view)) {
touchHandler.setView(view); return;
} }
if (keyHandler != null) {
keyHandler.setView(view); if (this.view != null) {
} removeListeners(this.view);
if (gestureHandler != null) {
gestureHandler.setView(view);
} }
this.view = (GLSurfaceView)view; this.view = (GLSurfaceView)view;
}
public View getView() { if (this.view != null) {
return view; addListeners(this.view);
} }
public float invertX(float origX) { joyInput.setView((GLSurfaceView)view);
return getJmeX(view.getWidth()) - origX;
} }
public float invertY(float origY) { public View getView() {
return getJmeY(view.getHeight()) - origY; return view;
} }
public float getJmeX(float origX) { protected void removeListeners(GLSurfaceView view) {
return origX * scaleX; view.setOnTouchListener(null);
view.setOnKeyListener(null);
touchInput.setGestureDetector(null);
touchInput.setScaleDetector(null);
} }
public float getJmeY(float origY) { protected void addListeners(GLSurfaceView view) {
return origY * scaleY; 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) { public void loadSettings(AppSettings settings) {
keyboardEventsEnabled = settings.isEmulateKeyboard(); touchInput.loadSettings(settings);
mouseEventsEnabled = settings.isEmulateMouse(); }
mouseEventsInvertX = settings.isEmulateMouseFlipX();
mouseEventsInvertY = settings.isEmulateMouseFlipY(); public TouchInput getTouchInput() {
return touchInput;
// 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(); public JoyInput getJoyInput() {
scaleY = (float)settings.getHeight() / (float)view.getHeight(); return joyInput;
} }
logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}",
new Object[]{scaleX, scaleY}); /*
* 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 @Override
public void initialize() { public boolean onTouch(View view, MotionEvent event) {
touchEventPool.initialize(); if (view != getView()) {
if (touchHandler != null) { return false;
touchHandler.initialize();
}
if (keyHandler != null) {
keyHandler.initialize();
}
if (gestureHandler != null) {
gestureHandler.initialize();
} }
initialized = true; boolean consumed = false;
}
@Override int source = event.getSource();
public void destroy() { // logger.log(Level.INFO, "onTouch source: {0}", source);
initialized = false;
touchEventPool.destroy(); boolean isTouch = ((source & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN);
if (touchHandler != null) { // logger.log(Level.INFO, "onTouch source: {0}, isTouch: {1}",
touchHandler.destroy(); // new Object[]{source, isTouch});
}
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();
}
public void update() { if (isTouch && touchInput != null) {
if (listener != null) { // send the event to the touch processor
InputEvent inputEvent; consumed = touchInput.onTouch(event);
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);
}
}
} }
}
// ----------------------------------------- return consumed;
public TouchEvent getFreeTouchEvent() {
return touchEventPool.getNextFreeEvent();
} }
public void addEvent(InputEvent event) { @Override
inputEventQueue.add(event); public boolean onKey(View view, int keyCode, KeyEvent event) {
if (event instanceof TouchEvent) { if (view != getView()) {
touchEventPool.storeEvent((TouchEvent)event); return false;
} }
}
public void setSimulateMouse(boolean simulate) { boolean consumed = false;
this.mouseEventsEnabled = simulate;
}
public boolean isSimulateMouse() { int source = event.getSource();
return mouseEventsEnabled; // logger.log(Level.INFO, "onKey source: {0}", source);
}
public boolean isMouseEventsInvertX() { boolean isTouch =
return mouseEventsInvertX; ((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() { if (touchInput != null) {
return mouseEventsInvertY; consumed = touchInput.onKey(event);
} }
public void setSimulateKeyboard(boolean simulate) {
this.keyboardEventsEnabled = simulate;
}
public boolean isSimulateKeyboard() { return consumed;
return keyboardEventsEnabled;
}
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.content.Context;
import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Vibrator; import android.os.Vibrator;
import android.view.View;
import com.jme3.input.InputManager; import com.jme3.input.InputManager;
import com.jme3.input.JoyInput; import com.jme3.input.JoyInput;
import com.jme3.input.Joystick; import com.jme3.input.Joystick;
@ -79,15 +77,15 @@ import java.util.logging.Logger;
* *
* @author iwgeric * @author iwgeric
*/ */
public class AndroidJoyInputHandler implements JoyInput { public class AndroidJoyInput implements JoyInput {
private static final Logger logger = Logger.getLogger(AndroidJoyInputHandler.class.getName()); 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; // private boolean dontSendHistory = false;
// Internal // Internal
private GLSurfaceView view;
private boolean initialized = false; private boolean initialized = false;
private RawInputListener listener = null; private RawInputListener listener = null;
private ConcurrentLinkedQueue<InputEvent> eventQueue = new ConcurrentLinkedQueue<InputEvent>(); private ConcurrentLinkedQueue<InputEvent> eventQueue = new ConcurrentLinkedQueue<InputEvent>();
@ -96,34 +94,29 @@ public class AndroidJoyInputHandler implements JoyInput {
private boolean vibratorActive = false; private boolean vibratorActive = false;
private long maxRumbleTime = 250; // 250ms private long maxRumbleTime = 250; // 250ms
public AndroidJoyInputHandler() { public AndroidJoyInput(AndroidInputHandler inputHandler) {
int buildVersion = Build.VERSION.SDK_INT; this.inputHandler = inputHandler;
logger.log(Level.INFO, "Android Build Version: {0}", buildVersion);
// if (buildVersion >= 14) {
// touchHandler = new AndroidTouchHandler14(this);
// } else if (buildVersion >= 8){
// touchHandler = new AndroidTouchHandler(this);
// }
sensorJoyInput = new AndroidSensorJoyInput(this); sensorJoyInput = new AndroidSensorJoyInput(this);
} }
public void setView(GLSurfaceView view) { public void setView(GLSurfaceView view) {
// if (touchHandler != null) { if (view == null) {
// touchHandler.setView(view); 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) { if (sensorJoyInput != null) {
sensorJoyInput.setView(view); sensorJoyInput.setView(view);
} }
this.view = (GLSurfaceView)view;
}
public View getView() {
return view;
} }
public void loadSettings(AppSettings settings) { public void loadSettings(AppSettings settings) {
// sensorEventsEnabled = settings.useSensors();
} }
public void addEvent(InputEvent event) { public void addEvent(InputEvent event) {
@ -155,20 +148,8 @@ public class AndroidJoyInputHandler implements JoyInput {
} }
@Override @Override
public void initialize() { 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; initialized = true;
} }
@ -211,8 +192,8 @@ public class AndroidJoyInputHandler implements JoyInput {
}; };
final int rumbleRepeatFrom = 0; // index into rumble pattern to repeat from final int rumbleRepeatFrom = 0; // index into rumble pattern to repeat from
logger.log(Level.FINE, "Rumble amount: {0}, rumbleOnDur: {1}, rumbleOffDur: {2}", // logger.log(Level.FINE, "Rumble amount: {0}, rumbleOnDur: {1}, rumbleOffDur: {2}",
new Object[]{amount, rumbleOnDur, rumbleOffDur}); // new Object[]{amount, rumbleOnDur, rumbleOffDur});
if (rumbleOnDur > 0) { if (rumbleOnDur > 0) {
vibrator.vibrate(rumblePattern, rumbleRepeatFrom); vibrator.vibrate(rumblePattern, rumbleRepeatFrom);
@ -226,9 +207,8 @@ public class AndroidJoyInputHandler implements JoyInput {
@Override @Override
public Joystick[] loadJoysticks(InputManager inputManager) { public Joystick[] loadJoysticks(InputManager inputManager) {
logger.log(Level.INFO, "loading joysticks for {0}", this.getClass().getName());
joystickList.add(sensorJoyInput.loadJoystick(joystickList.size(), inputManager)); joystickList.add(sensorJoyInput.loadJoystick(joystickList.size(), inputManager));
return joystickList.toArray( new Joystick[joystickList.size()] ); 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 * 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 * @author iwgeric
*/ */
public class AndroidKeyMapping { public class AndroidKeyMapping {
private static final Logger logger = Logger.getLogger(AndroidKeyMapping.class.getName()); private static final Logger logger = Logger.getLogger(AndroidKeyMapping.class.getName());
private static final int[] ANDROID_TO_JME = { private static final int[] ANDROID_TO_JME = {
0x0, // unknown 0x0, // unknown
0x0, // key code soft left 0x0, // key code soft left
@ -141,9 +142,13 @@ public class AndroidKeyMapping {
0x0,//media fastforward 0x0,//media fastforward
0x0,//mute 0x0,//mute
}; };
public static int getJmeKey(int androidKey) { 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 { public class AndroidSensorJoyInput implements SensorEventListener {
private final static Logger logger = Logger.getLogger(AndroidSensorJoyInput.class.getName()); private final static Logger logger = Logger.getLogger(AndroidSensorJoyInput.class.getName());
private AndroidJoyInputHandler joyHandler; private AndroidJoyInput joyInput;
private SensorManager sensorManager = null; private SensorManager sensorManager = null;
private WindowManager windowManager = null; private WindowManager windowManager = null;
private IntMap<SensorData> sensors = new IntMap<SensorData>(); private IntMap<SensorData> sensors = new IntMap<SensorData>();
private int lastRotation = 0; private int lastRotation = 0;
private boolean loaded = false; private boolean loaded = false;
public AndroidSensorJoyInput(AndroidJoyInputHandler joyHandler) { public AndroidSensorJoyInput(AndroidJoyInput joyInput) {
this.joyHandler = joyHandler; this.joyInput = joyInput;
} }
/** /**
@ -96,7 +96,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
int sensorAccuracy = -1; int sensorAccuracy = -1;
float[] lastValues; float[] lastValues;
final Object valuesLock = new Object(); final Object valuesLock = new Object();
ArrayList<AndroidJoystickAxis> axes = new ArrayList<AndroidJoystickAxis>(); ArrayList<AndroidSensorJoystickAxis> axes = new ArrayList<AndroidSensorJoystickAxis>();
boolean enabled = false; boolean enabled = false;
boolean haveData = false; boolean haveData = false;
@ -306,7 +306,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
*/ */
private boolean updateOrientation() { private boolean updateOrientation() {
SensorData sensorData; SensorData sensorData;
AndroidJoystickAxis axis; AndroidSensorJoystickAxis axis;
final float[] curInclinationMat = new float[16]; final float[] curInclinationMat = new float[16];
final float[] curRotationMat = new float[16]; final float[] curRotationMat = new float[16];
final float[] rotatedRotationMat = new float[16]; final float[] rotatedRotationMat = new float[16];
@ -374,7 +374,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
sensorData.haveData = true; sensorData.haveData = true;
} else { } else {
if (axis.isChanged()) { 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) { public Joystick loadJoystick(int joyId, InputManager inputManager) {
SensorData sensorData; SensorData sensorData;
AndroidJoystickAxis axis; AndroidSensorJoystickAxis axis;
AndroidJoystick joystick = new AndroidJoystick(inputManager, AndroidSensorJoystick joystick = new AndroidSensorJoystick(inputManager,
joyHandler, joyInput,
joyId, joyId,
"AndroidSensorsJoystick"); "AndroidSensorsJoystick");
@ -522,15 +522,15 @@ public class AndroidSensorJoyInput implements SensorEventListener {
if (!loaded) { if (!loaded) {
return; return;
} }
logger.log(Level.FINE, "onSensorChanged for {0}: accuracy: {1}, values: {2}", // logger.log(Level.FINE, "onSensorChanged for {0}: accuracy: {1}, values: {2}",
new Object[]{se.sensor.getName(), se.accuracy, se.values}); // new Object[]{se.sensor.getName(), se.accuracy, se.values});
int sensorType = se.sensor.getType(); int sensorType = se.sensor.getType();
SensorData sensorData = sensors.get(sensorType); SensorData sensorData = sensors.get(sensorType);
if (sensorData != null) { if (sensorData != null) {
logger.log(Level.FINE, "sensorData name: {0}, enabled: {1}, unreliable: {2}", // logger.log(Level.FINE, "sensorData name: {0}, enabled: {1}, unreliable: {2}",
new Object[]{sensorData.sensor.getName(), sensorData.enabled, sensorData.sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE}); // new Object[]{sensorData.sensor.getName(), sensorData.enabled, sensorData.sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE});
} }
if (sensorData != null && sensorData.sensor.equals(se.sensor) && sensorData.enabled) { 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) { if (sensorData.axes.size() > 0) {
AndroidJoystickAxis axis; AndroidSensorJoystickAxis axis;
for (int i=0; i<se.values.length; i++) { for (int i=0; i<se.values.length; i++) {
axis = sensorData.axes.get(i); axis = sensorData.axes.get(i);
if (axis != null) { if (axis != null) {
@ -554,8 +554,8 @@ public class AndroidSensorJoyInput implements SensorEventListener {
} else { } else {
if (axis.isChanged()) { if (axis.isChanged()) {
JoyAxisEvent event = new JoyAxisEvent(axis, axis.getJoystickAxisValue()); JoyAxisEvent event = new JoyAxisEvent(axis, axis.getJoystickAxisValue());
logger.log(Level.INFO, "adding JoyAxisEvent: {0}", event); // logger.log(Level.INFO, "adding JoyAxisEvent: {0}", event);
joyHandler.addEvent(event); joyInput.addEvent(event);
// joyHandler.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue())); // joyHandler.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue()));
} }
} }
@ -585,14 +585,14 @@ public class AndroidSensorJoyInput implements SensorEventListener {
// End of SensorEventListener methods // End of SensorEventListener methods
protected class AndroidJoystick extends AbstractJoystick { protected class AndroidSensorJoystick extends AbstractJoystick {
private JoystickAxis nullAxis; private JoystickAxis nullAxis;
private JoystickAxis xAxis; private JoystickAxis xAxis;
private JoystickAxis yAxis; private JoystickAxis yAxis;
private JoystickAxis povX; private JoystickAxis povX;
private JoystickAxis povY; private JoystickAxis povY;
public AndroidJoystick( InputManager inputManager, JoyInput joyInput, public AndroidSensorJoystick( InputManager inputManager, JoyInput joyInput,
int joyId, String name){ int joyId, String name){
super( inputManager, joyInput, joyId, 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) { protected AndroidSensorJoystickAxis addAxis(String axisName, String logicalName, int axisNum, float maxRawValue) {
AndroidJoystickAxis axis; AndroidSensorJoystickAxis axis;
axis = new AndroidJoystickAxis( axis = new AndroidSensorJoystickAxis(
getInputManager(), // InputManager (InputManager) getInputManager(), // InputManager (InputManager)
this, // parent Joystick (Joystick) this, // parent Joystick (Joystick)
axisNum, // Axis Index (int) 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 zeroRawValue = 0f;
float curRawValue = 0f; float curRawValue = 0f;
float lastRawValue = 0f; float lastRawValue = 0f;
@ -662,7 +662,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
float maxRawValue = FastMath.HALF_PI; float maxRawValue = FastMath.HALF_PI;
boolean enabled = true; boolean enabled = true;
public AndroidJoystickAxis(InputManager inputManager, Joystick parent, public AndroidSensorJoystickAxis(InputManager inputManager, Joystick parent,
int axisIndex, String name, String logicalId, int axisIndex, String name, String logicalId,
boolean isAnalog, boolean isRelative, float deadZone, boolean isAnalog, boolean isRelative, float deadZone,
float maxRawValue) { 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; package com.jme3.input.android;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View;
import com.jme3.input.event.TouchEvent; import com.jme3.input.event.TouchEvent;
import com.jme3.math.Vector2f; import com.jme3.math.Vector2f;
import java.util.HashMap; import java.util.HashMap;
@ -41,36 +40,20 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
* AndroidTouchHandler14 is an extension of AndroidTouchHander that adds the * AndroidTouchHandler14 extends AndroidTouchHandler to process the onHover
* Android touch event functionality between Android rev 9 (Android 2.3) and * events added in Android rev 14 (Android 4.0).
* Android rev 14 (Android 4.0). *
*
* @author iwgeric * @author iwgeric
*/ */
public class AndroidTouchHandler14 extends AndroidTouchHandler implements public class AndroidTouchInput14 extends AndroidTouchInput {
View.OnHoverListener { private static final Logger logger = Logger.getLogger(AndroidTouchInput14.class.getName());
private static final Logger logger = Logger.getLogger(AndroidTouchHandler14.class.getName());
final private HashMap<Integer, Vector2f> lastHoverPositions = new HashMap<Integer, Vector2f>(); final private HashMap<Integer, Vector2f> lastHoverPositions = new HashMap<Integer, Vector2f>();
public AndroidTouchHandler14(AndroidInputHandler androidInput, AndroidGestureHandler gestureHandler) {
super(androidInput, gestureHandler);
}
@Override public AndroidTouchInput14(AndroidInputHandler androidInput) {
public void setView(View view) { super(androidInput);
if (view != null) {
view.setOnHoverListener(this);
} else {
androidInput.getView().setOnHoverListener(null);
}
super.setView(view);
} }
public boolean onHover(View view, MotionEvent event) { public boolean onHover(MotionEvent event) {
if (view == null || view != androidInput.getView()) {
return false;
}
boolean consumed = false; boolean consumed = false;
int action = getAction(event); int action = getAction(event);
int pointerId = getPointerId(event); int pointerId = getPointerId(event);
@ -78,34 +61,34 @@ public class AndroidTouchHandler14 extends AndroidTouchHandler implements
Vector2f lastPos = lastHoverPositions.get(pointerId); Vector2f lastPos = lastHoverPositions.get(pointerId);
float jmeX; float jmeX;
float jmeY; float jmeY;
numPointers = event.getPointerCount(); numPointers = event.getPointerCount();
logger.log(Level.INFO, "onHover pointerId: {0}, action: {1}, x: {2}, y: {3}, numPointers: {4}", // 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()}); // new Object[]{pointerId, action, event.getX(), event.getY(), event.getPointerCount()});
TouchEvent touchEvent; TouchEvent touchEvent;
switch (action) { switch (action) {
case MotionEvent.ACTION_HOVER_ENTER: case MotionEvent.ACTION_HOVER_ENTER:
jmeX = androidInput.getJmeX(event.getX(pointerIndex)); jmeX = getJmeX(event.getX(pointerIndex));
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex))); jmeY = invertY(getJmeY(event.getY(pointerIndex)));
touchEvent = androidInput.getFreeTouchEvent(); touchEvent = getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.HOVER_START, jmeX, jmeY, 0, 0); touchEvent.set(TouchEvent.Type.HOVER_START, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(pointerId); touchEvent.setPointerId(pointerId);
touchEvent.setTime(event.getEventTime()); touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure(pointerIndex)); touchEvent.setPressure(event.getPressure(pointerIndex));
lastPos = new Vector2f(jmeX, jmeY); lastPos = new Vector2f(jmeX, jmeY);
lastHoverPositions.put(pointerId, lastPos); lastHoverPositions.put(pointerId, lastPos);
processEvent(touchEvent); addEvent(touchEvent);
consumed = true; consumed = true;
break; break;
case MotionEvent.ACTION_HOVER_MOVE: case MotionEvent.ACTION_HOVER_MOVE:
// Convert all pointers into events // Convert all pointers into events
for (int p = 0; p < event.getPointerCount(); p++) { for (int p = 0; p < event.getPointerCount(); p++) {
jmeX = androidInput.getJmeX(event.getX(p)); jmeX = getJmeX(event.getX(p));
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(p))); jmeY = invertY(getJmeY(event.getY(p)));
lastPos = lastHoverPositions.get(event.getPointerId(p)); lastPos = lastHoverPositions.get(event.getPointerId(p));
if (lastPos == null) { if (lastPos == null) {
lastPos = new Vector2f(jmeX, jmeY); lastPos = new Vector2f(jmeX, jmeY);
@ -115,38 +98,39 @@ public class AndroidTouchHandler14 extends AndroidTouchHandler implements
float dX = jmeX - lastPos.x; float dX = jmeX - lastPos.x;
float dY = jmeY - lastPos.y; float dY = jmeY - lastPos.y;
if (dX != 0 || dY != 0) { if (dX != 0 || dY != 0) {
touchEvent = androidInput.getFreeTouchEvent(); touchEvent = getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.HOVER_MOVE, jmeX, jmeY, dX, dY); touchEvent.set(TouchEvent.Type.HOVER_MOVE, jmeX, jmeY, dX, dY);
touchEvent.setPointerId(event.getPointerId(p)); touchEvent.setPointerId(event.getPointerId(p));
touchEvent.setTime(event.getEventTime()); touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure(p)); touchEvent.setPressure(event.getPressure(p));
lastPos.set(jmeX, jmeY); lastPos.set(jmeX, jmeY);
processEvent(touchEvent); addEvent(touchEvent);
} }
} }
consumed = true; consumed = true;
break; break;
case MotionEvent.ACTION_HOVER_EXIT: case MotionEvent.ACTION_HOVER_EXIT:
jmeX = androidInput.getJmeX(event.getX(pointerIndex)); jmeX = getJmeX(event.getX(pointerIndex));
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex))); jmeY = invertY(getJmeY(event.getY(pointerIndex)));
touchEvent = androidInput.getFreeTouchEvent(); touchEvent = getFreeTouchEvent();
touchEvent.set(TouchEvent.Type.HOVER_END, jmeX, jmeY, 0, 0); touchEvent.set(TouchEvent.Type.HOVER_END, jmeX, jmeY, 0, 0);
touchEvent.setPointerId(pointerId); touchEvent.setPointerId(pointerId);
touchEvent.setTime(event.getEventTime()); touchEvent.setTime(event.getEventTime());
touchEvent.setPressure(event.getPressure(pointerIndex)); touchEvent.setPressure(event.getPressure(pointerIndex));
lastHoverPositions.remove(pointerId); lastHoverPositions.remove(pointerId);
processEvent(touchEvent); addEvent(touchEvent);
consumed = true; consumed = true;
break; break;
default: default:
consumed = false; consumed = false;
break; break;
} }
return consumed; return consumed;
} }
} }

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

Loading…
Cancel
Save