aboutsummaryrefslogtreecommitdiff
path: root/engine/src/android/com/jme3/input/android/AndroidInput.java
diff options
context:
space:
mode:
authorScott Barta <sbarta@google.com>2012-03-01 12:35:35 -0800
committerScott Barta <sbarta@google.com>2012-03-01 12:40:08 -0800
commit59b2e6871c65f58fdad78cd7229c292f6a177578 (patch)
tree2d4e7bfc05b93f40b34675d77e403dd1c25efafd /engine/src/android/com/jme3/input/android/AndroidInput.java
parentf9b30489e75ac1eabc365064959804e99534f7ab (diff)
downloadjmonkeyengine-59b2e6871c65f58fdad78cd7229c292f6a177578.tar.gz
Adds the jMonkeyEngine library to the build.
Adds the jMonkeyEngine open source 3D game engine to the build. This is built as a static library and is only used by the Finsky client. Change-Id: I06a3f054df7b8a67757267d884854f70c5a16ca0
Diffstat (limited to 'engine/src/android/com/jme3/input/android/AndroidInput.java')
-rw-r--r--engine/src/android/com/jme3/input/android/AndroidInput.java619
1 files changed, 619 insertions, 0 deletions
diff --git a/engine/src/android/com/jme3/input/android/AndroidInput.java b/engine/src/android/com/jme3/input/android/AndroidInput.java
new file mode 100644
index 0000000..ca1141d
--- /dev/null
+++ b/engine/src/android/com/jme3/input/android/AndroidInput.java
@@ -0,0 +1,619 @@
+package com.jme3.input.android;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+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.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 extends GLSurfaceView implements TouchInput,
+ 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 ScaleGestureDetector scaledetector;
+ 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(Context ctx, AttributeSet attribs) {
+ super(ctx, attribs);
+ detector = new GestureDetector(null, this, null, false);
+ scaledetector = new ScaleGestureDetector(ctx, this);
+
+ }
+
+ public AndroidInput(Context ctx) {
+ super(ctx);
+ detector = new GestureDetector(null, this, null, false);
+ scaledetector = new ScaleGestureDetector(ctx, 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;
+ }
+
+ /**
+ * onTouchEvent gets called from android thread on touchpad events
+ */
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ 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);
+
+ // 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), this.getHeight() - event.getY(pointerIndex), 0, 0);
+ touch.setPointerId(pointerId);
+ touch.setTime(event.getEventTime());
+ touch.setPressure(event.getPressure(pointerIndex));
+ processEvent(touch);
+
+ 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), this.getHeight() - event.getY(pointerIndex), 0, 0);
+ touch.setPointerId(pointerId);
+ touch.setTime(event.getEventTime());
+ touch.setPressure(event.getPressure(pointerIndex));
+ processEvent(touch);
+
+
+ bWasHandled = true;
+ break;
+ case MotionEvent.ACTION_MOVE:
+
+
+ // Convert all pointers into events
+ for (int p = 0; p < event.getPointerCount(); p++) {
+ Vector2f lastPos = lastPositions.get(pointerIndex);
+ if (lastPos == null) {
+ lastPos = new Vector2f(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));
+ lastPositions.put(pointerId, lastPos);
+ }
+ touch = getNextFreeTouchEvent();
+ touch.set(Type.MOVE, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), event.getX(pointerIndex) - lastPos.x, this.getHeight() - event.getY(pointerIndex) - lastPos.y);
+ touch.setPointerId(pointerId);
+ touch.setTime(event.getEventTime());
+ touch.setPressure(event.getPressure(pointerIndex));
+ processEvent(touch);
+ lastPos.set(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));
+ }
+ bWasHandled = true;
+ break;
+
+
+ case MotionEvent.ACTION_OUTSIDE:
+ break;
+
+ }
+
+ // Try to detect gestures
+ this.detector.onTouchEvent(event);
+ this.scaledetector.onTouchEvent(event);
+
+ return bWasHandled;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ 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;
+ }
+
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ 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;
+ }
+ }
+
+ // -----------------------------------------
+ // 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();
+ }
+ }
+
+ @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) {
+ eventQueue.push(event);
+ }
+ }
+
+ // --------------- INSIDE GLThread ---------------
+ @Override
+ public void update() {
+ generateEvents();
+ }
+
+ private void generateEvents() {
+ if (listener != null) {
+ TouchEvent event;
+ MouseButtonEvent btn;
+ int newX;
+ int newY;
+
+ while (!eventQueue.isEmpty()) {
+ synchronized (eventQueue) {
+ event = eventQueue.pop();
+ }
+ if (event != null) {
+ listener.onTouchEvent(event);
+
+ if (mouseEventsEnabled) {
+ if (mouseEventsInvertX) {
+ newX = this.getWidth() - (int) event.getX();
+ } else {
+ newX = (int) event.getX();
+ }
+
+ if (mouseEventsInvertY) {
+ newY = this.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 MOVE:
+ int dx;
+ int dy;
+ if (lastX != -1) {
+ dx = newX - lastX;
+ dy = newY - lastY;
+ } else {
+ dx = 0;
+ dy = 0;
+ }
+ MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0);
+ 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(), this.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(), this.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(), this.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) {
+ 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.setScaleFactor(scaleGestureDetector.getScaleFactor());
+ processEvent(touch);
+ // System.out.println("scaleBegin");
+
+ return true;
+ }
+
+ public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
+ TouchEvent touch = getNextFreeTouchEvent();
+ touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
+ touch.setPointerId(0);
+ touch.setTime(scaleGestureDetector.getEventTime());
+ touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
+ touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
+ processEvent(touch);
+ // System.out.println("scale");
+
+ return false;
+ }
+
+ public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
+ TouchEvent touch = getNextFreeTouchEvent();
+ touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
+ touch.setPointerId(0);
+ touch.setTime(scaleGestureDetector.getEventTime());
+ touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
+ touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
+ processEvent(touch);
+ }
+
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ TouchEvent touch = getNextFreeTouchEvent();
+ touch.set(Type.SCROLL, e1.getX(), this.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(), this.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(), this.getHeight() - event.getY(), 0f, 0f);
+ touch.setPointerId(0);
+ touch.setTime(event.getEventTime());
+ processEvent(touch);
+ return true;
+ }
+
+ @Override
+ public void setSimulateMouse(boolean simulate) {
+ mouseEventsEnabled = simulate;
+ }
+
+ @Override
+ public void setSimulateKeyboard(boolean simulate) {
+ keyboardEventsEnabled = simulate;
+ }
+
+ @Override
+ public void setOmitHistoricEvents(boolean dontSendHistory) {
+ this.dontSendHistory = dontSendHistory;
+ }
+
+ // TODO: move to TouchInput
+ public boolean isMouseEventsEnabled() {
+ return mouseEventsEnabled;
+ }
+
+ 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;
+ }
+}