Quellcode durchsuchen

Rework of Android input system to support future expansion and gamepad support.
Gamepad support is still a work in progress, but functions.

iwgeric vor 10 Jahren
Ursprung
Commit
61ba11d872

+ 100 - 174
jme3-android/src/main/java/com/jme3/input/android/AndroidGestureHandler.java → jme3-android/src/main/java/com/jme3/input/android/AndroidGestureProcessor.java

@@ -35,314 +35,240 @@ package com.jme3.input.android;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 import android.view.ScaleGestureDetector;
-import android.view.View;
-import com.jme3.input.event.InputEvent;
-import com.jme3.input.event.MouseMotionEvent;
 import com.jme3.input.event.TouchEvent;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 /**
  * AndroidGestureHandler uses Gesture type listeners to create jME TouchEvents
- * for gestures.  This class is designed to handle the gestures supported 
+ * for gestures.  This class is designed to handle the gestures supported
  * on Android rev 9 (Android 2.3).  Extend this class to add functionality
  * added by Android after rev 9.
- * 
+ *
  * @author iwgeric
  */
-public class AndroidGestureHandler implements 
-        GestureDetector.OnGestureListener, 
+public class AndroidGestureProcessor implements
+        GestureDetector.OnGestureListener,
         GestureDetector.OnDoubleTapListener,
         ScaleGestureDetector.OnScaleGestureListener {
-    private static final Logger logger = Logger.getLogger(AndroidGestureHandler.class.getName());
-    private AndroidInputHandler androidInput;
-    private GestureDetector gestureDetector;
-    private ScaleGestureDetector scaleDetector;
+    private static final Logger logger = Logger.getLogger(AndroidGestureProcessor.class.getName());
+
+    private AndroidTouchInput touchInput;
     float gestureDownX = -1f;
     float gestureDownY = -1f;
     float scaleStartX = -1f;
     float scaleStartY = -1f;
 
-    public AndroidGestureHandler(AndroidInputHandler androidInput) {
-        this.androidInput = androidInput;
-    }
-    
-    public void initialize() {
-    }
-    
-    public void destroy() {
-        setView(null);
-    }
-    
-    public void setView(View view) {
-        if (view != null) {
-            gestureDetector = new GestureDetector(view.getContext(), this);
-            scaleDetector = new ScaleGestureDetector(view.getContext(), this);
-        } else {
-            gestureDetector = null;
-            scaleDetector = null;
-        }
-    }
-    
-    public void detectGesture(MotionEvent event) {
-        if (gestureDetector != null && scaleDetector != null) {
-            gestureDetector.onTouchEvent(event);
-            scaleDetector.onTouchEvent(event);
-        }
-    }
-
-    private int getPointerIndex(MotionEvent event) {
-        return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
-                >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-    }
-    
-    private int getPointerId(MotionEvent event) {
-        return event.getPointerId(getPointerIndex(event));
+    public AndroidGestureProcessor(AndroidTouchInput touchInput) {
+        this.touchInput = touchInput;
     }
-    
-    private void processEvent(TouchEvent event) {
-        // Add the touch event
-        androidInput.addEvent(event);
-        if (androidInput.isSimulateMouse()) {
-            InputEvent mouseEvent = generateMouseEvent(event);
-            if (mouseEvent != null) {
-                // Add the mouse event
-                androidInput.addEvent(mouseEvent);
-            }
-        }
-    }
-
-    // TODO: Ring Buffer for mouse events?
-    private InputEvent generateMouseEvent(TouchEvent event) {
-        InputEvent inputEvent = null;
-        int newX;
-        int newY;
-        int newDX;
-        int newDY;
 
-        if (androidInput.isMouseEventsInvertX()) {
-            newX = (int) (androidInput.invertX(event.getX()));
-            newDX = (int)event.getDeltaX() * -1;
-        } else {
-            newX = (int) event.getX();
-            newDX = (int)event.getDeltaX();
-        }
-        int wheel = (int) (event.getScaleSpan()); // might need to scale to match mouse wheel
-        int dWheel = (int) (event.getDeltaScaleSpan()); // might need to scale to match mouse wheel
-
-        if (androidInput.isMouseEventsInvertY()) {
-            newY = (int) (androidInput.invertY(event.getY()));
-            newDY = (int)event.getDeltaY() * -1;
-        } else {
-            newY = (int) event.getY();
-            newDY = (int)event.getDeltaY();
-        }
-
-        switch (event.getType()) {
-            case SCALE_MOVE:
-                inputEvent = new MouseMotionEvent(newX, newY, newDX, newDY, wheel, dWheel);
-                inputEvent.setTime(event.getTime());
-                break;
-        }
-
-        return inputEvent;
-    }
-    
     /* Events from onGestureListener */
-    
+
+    @Override
     public boolean onDown(MotionEvent event) {
         // start of all GestureListeners.  Not really a gesture by itself
         // so we don't create an event.
         // However, reset the scaleInProgress here since this is the beginning
         // of a series of gesture events.
-//        logger.log(Level.INFO, "onDown pointerId: {0}, action: {1}, x: {2}, y: {3}", 
-//                new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
-        gestureDownX = androidInput.getJmeX(event.getX());
-        gestureDownY = androidInput.invertY(androidInput.getJmeY(event.getY()));
+//        logger.log(Level.INFO, "onDown pointerId: {0}, action: {1}, x: {2}, y: {3}",
+//                new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
+        gestureDownX = touchInput.getJmeX(event.getX());
+        gestureDownY = touchInput.invertY(touchInput.getJmeY(event.getY()));
         return true;
     }
 
+    @Override
     public boolean onSingleTapUp(MotionEvent event) {
         // Up of single tap.  May be followed by a double tap later.
         // use onSingleTapConfirmed instead.
-//        logger.log(Level.INFO, "onSingleTapUp pointerId: {0}, action: {1}, x: {2}, y: {3}", 
-//                new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
+//        logger.log(Level.INFO, "onSingleTapUp pointerId: {0}, action: {1}, x: {2}, y: {3}",
+//                new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
         return true;
     }
 
+    @Override
     public void onShowPress(MotionEvent event) {
-//        logger.log(Level.INFO, "onShowPress pointerId: {0}, action: {1}, x: {2}, y: {3}", 
-//                new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
-        float jmeX = androidInput.getJmeX(event.getX());
-        float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
-        TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+//        logger.log(Level.INFO, "onShowPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
+//                new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
+        float jmeX = touchInput.getJmeX(event.getX());
+        float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
+        TouchEvent touchEvent = touchInput.getFreeTouchEvent();
         touchEvent.set(TouchEvent.Type.SHOWPRESS, jmeX, jmeY, 0, 0);
-        touchEvent.setPointerId(getPointerId(event));
+        touchEvent.setPointerId(touchInput.getPointerId(event));
         touchEvent.setTime(event.getEventTime());
         touchEvent.setPressure(event.getPressure());
-        processEvent(touchEvent);
+        touchInput.addEvent(touchEvent);
     }
 
+    @Override
     public void onLongPress(MotionEvent event) {
-//        logger.log(Level.INFO, "onLongPress pointerId: {0}, action: {1}, x: {2}, y: {3}", 
-//                new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
-        float jmeX = androidInput.getJmeX(event.getX());
-        float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
-        TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+//        logger.log(Level.INFO, "onLongPress pointerId: {0}, action: {1}, x: {2}, y: {3}",
+//                new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
+        float jmeX = touchInput.getJmeX(event.getX());
+        float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
+        TouchEvent touchEvent = touchInput.getFreeTouchEvent();
         touchEvent.set(TouchEvent.Type.LONGPRESSED, jmeX, jmeY, 0, 0);
-        touchEvent.setPointerId(getPointerId(event));
+        touchEvent.setPointerId(touchInput.getPointerId(event));
         touchEvent.setTime(event.getEventTime());
         touchEvent.setPressure(event.getPressure());
-        processEvent(touchEvent);
+        touchInput.addEvent(touchEvent);
     }
 
+    @Override
     public boolean onScroll(MotionEvent startEvent, MotionEvent endEvent, float distX, float distY) {
         // if not scaleInProgess, send scroll events.  This is to avoid sending
         // scroll events when one of the fingers is lifted just before the other one.
         // Avoids sending the scroll for that brief period of time.
         // Return true so that the next event doesn't accumulate the distX and distY values.
-        // Apparantly, both distX and distY are negative.  
+        // Apparantly, both distX and distY are negative.
         // Negate distX to get the real value, but leave distY negative to compensate
         // for the fact that jME has y=0 at bottom where Android has y=0 at top.
-//        if (!scaleInProgress) {
-        if (!scaleDetector.isInProgress()) {
-//            logger.log(Level.INFO, "onScroll pointerId: {0}, startAction: {1}, startX: {2}, startY: {3}, endAction: {4}, endX: {5}, endY: {6}, dx: {7}, dy: {8}", 
-//                    new Object[]{getPointerId(startEvent), getAction(startEvent), startEvent.getX(), startEvent.getY(), getAction(endEvent), endEvent.getX(), endEvent.getY(), distX, distY});
+        if (!touchInput.getScaleDetector().isInProgress()) {
+//            logger.log(Level.INFO, "onScroll pointerId: {0}, startAction: {1}, startX: {2}, startY: {3}, endAction: {4}, endX: {5}, endY: {6}, dx: {7}, dy: {8}",
+//                    new Object[]{touchInput.getPointerId(startEvent), touchInput.getAction(startEvent), startEvent.getX(), startEvent.getY(), touchInput.getAction(endEvent), endEvent.getX(), endEvent.getY(), distX, distY});
 
-            float jmeX = androidInput.getJmeX(endEvent.getX());
-            float jmeY = androidInput.invertY(androidInput.getJmeY(endEvent.getY()));
-            TouchEvent touchEvent = androidInput.getFreeTouchEvent();
-            touchEvent.set(TouchEvent.Type.SCROLL, jmeX, jmeY, androidInput.getJmeX(-distX), androidInput.getJmeY(distY));
-            touchEvent.setPointerId(getPointerId(endEvent));
+            float jmeX = touchInput.getJmeX(endEvent.getX());
+            float jmeY = touchInput.invertY(touchInput.getJmeY(endEvent.getY()));
+            TouchEvent touchEvent = touchInput.getFreeTouchEvent();
+            touchEvent.set(TouchEvent.Type.SCROLL, jmeX, jmeY, touchInput.getJmeX(-distX), touchInput.getJmeY(distY));
+            touchEvent.setPointerId(touchInput.getPointerId(endEvent));
             touchEvent.setTime(endEvent.getEventTime());
             touchEvent.setPressure(endEvent.getPressure());
-            processEvent(touchEvent);
+            touchInput.addEvent(touchEvent);
         }
         return true;
     }
 
+    @Override
     public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float velocityX, float velocityY) {
         // Fling happens only once at the end of the gesture (all fingers up).
         // Fling returns the velocity of the finger movement in pixels/sec.
         // Therefore, the dX and dY values are actually velocity instead of distance values
         // Since this does not track the movement, use the start position and velocity values.
-        
-//        logger.log(Level.INFO, "onFling pointerId: {0}, startAction: {1}, startX: {2}, startY: {3}, endAction: {4}, endX: {5}, endY: {6}, velocityX: {7}, velocityY: {8}", 
-//                new Object[]{getPointerId(startEvent), getAction(startEvent), startEvent.getX(), startEvent.getY(), getAction(endEvent), endEvent.getX(), endEvent.getY(), velocityX, velocityY});
 
-        float jmeX = androidInput.getJmeX(startEvent.getX());
-        float jmeY = androidInput.invertY(androidInput.getJmeY(startEvent.getY()));
-        TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+//        logger.log(Level.INFO, "onFling pointerId: {0}, startAction: {1}, startX: {2}, startY: {3}, endAction: {4}, endX: {5}, endY: {6}, velocityX: {7}, velocityY: {8}",
+//                new Object[]{touchInput.getPointerId(startEvent), touchInput.getAction(startEvent), startEvent.getX(), startEvent.getY(), touchInput.getAction(endEvent), endEvent.getX(), endEvent.getY(), velocityX, velocityY});
+
+        float jmeX = touchInput.getJmeX(startEvent.getX());
+        float jmeY = touchInput.invertY(touchInput.getJmeY(startEvent.getY()));
+        TouchEvent touchEvent = touchInput.getFreeTouchEvent();
         touchEvent.set(TouchEvent.Type.FLING, jmeX, jmeY, velocityX, velocityY);
-        touchEvent.setPointerId(getPointerId(endEvent));
+        touchEvent.setPointerId(touchInput.getPointerId(endEvent));
         touchEvent.setTime(endEvent.getEventTime());
         touchEvent.setPressure(endEvent.getPressure());
-        processEvent(touchEvent);
+        touchInput.addEvent(touchEvent);
         return true;
     }
 
     /* Events from onDoubleTapListener */
-    
+
+    @Override
     public boolean onSingleTapConfirmed(MotionEvent event) {
         // Up of single tap when no double tap followed.
-//        logger.log(Level.INFO, "onSingleTapConfirmed pointerId: {0}, action: {1}, x: {2}, y: {3}", 
-//                new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
-        float jmeX = androidInput.getJmeX(event.getX());
-        float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
-        TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+//        logger.log(Level.INFO, "onSingleTapConfirmed pointerId: {0}, action: {1}, x: {2}, y: {3}",
+//                new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
+        float jmeX = touchInput.getJmeX(event.getX());
+        float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
+        TouchEvent touchEvent = touchInput.getFreeTouchEvent();
         touchEvent.set(TouchEvent.Type.TAP, jmeX, jmeY, 0, 0);
-        touchEvent.setPointerId(getPointerId(event));
+        touchEvent.setPointerId(touchInput.getPointerId(event));
         touchEvent.setTime(event.getEventTime());
         touchEvent.setPressure(event.getPressure());
-        processEvent(touchEvent);
+        touchInput.addEvent(touchEvent);
         return true;
     }
 
+    @Override
     public boolean onDoubleTap(MotionEvent event) {
         //The down motion event of the first tap of the double-tap
-        // We could use this event to fire off a double tap event, or use 
+        // We could use this event to fire off a double tap event, or use
         // DoubleTapEvent with a check for the UP action
-//        logger.log(Level.INFO, "onDoubleTap pointerId: {0}, action: {1}, x: {2}, y: {3}", 
-//                new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
-        float jmeX = androidInput.getJmeX(event.getX());
-        float jmeY = androidInput.invertY(androidInput.getJmeY(event.getY()));
-        TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+//        logger.log(Level.INFO, "onDoubleTap pointerId: {0}, action: {1}, x: {2}, y: {3}",
+//                new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
+        float jmeX = touchInput.getJmeX(event.getX());
+        float jmeY = touchInput.invertY(touchInput.getJmeY(event.getY()));
+        TouchEvent touchEvent = touchInput.getFreeTouchEvent();
         touchEvent.set(TouchEvent.Type.DOUBLETAP, jmeX, jmeY, 0, 0);
-        touchEvent.setPointerId(getPointerId(event));
+        touchEvent.setPointerId(touchInput.getPointerId(event));
         touchEvent.setTime(event.getEventTime());
         touchEvent.setPressure(event.getPressure());
-        processEvent(touchEvent);
+        touchInput.addEvent(touchEvent);
         return true;
     }
 
+    @Override
     public boolean onDoubleTapEvent(MotionEvent event) {
         //Notified when an event within a double-tap gesture occurs, including the down, move(s), and up events.
         // this means it will get called multiple times for a single double tap
-//        logger.log(Level.INFO, "onDoubleTapEvent pointerId: {0}, action: {1}, x: {2}, y: {3}", 
-//                new Object[]{getPointerId(event), getAction(event), event.getX(), event.getY()});
-//        if (getAction(event) == MotionEvent.ACTION_UP) {
-//            TouchEvent touchEvent = touchEventPool.getNextFreeEvent();
-//            touchEvent.set(TouchEvent.Type.DOUBLETAP, event.getX(), androidInput.invertY(event.getY()), 0, 0);
-//            touchEvent.setPointerId(getPointerId(event));
-//            touchEvent.setTime(event.getEventTime());
-//            touchEvent.setPressure(event.getPressure());
-//            processEvent(touchEvent);
-//        }
+//        logger.log(Level.INFO, "onDoubleTapEvent pointerId: {0}, action: {1}, x: {2}, y: {3}",
+//                new Object[]{touchInput.getPointerId(event), touchInput.getAction(event), event.getX(), event.getY()});
+        if (touchInput.getAction(event) == MotionEvent.ACTION_UP) {
+            TouchEvent touchEvent = touchInput.getFreeTouchEvent();
+            touchEvent.set(TouchEvent.Type.DOUBLETAP, event.getX(), touchInput.invertY(event.getY()), 0, 0);
+            touchEvent.setPointerId(touchInput.getPointerId(event));
+            touchEvent.setTime(event.getEventTime());
+            touchEvent.setPressure(event.getPressure());
+            touchInput.addEvent(touchEvent);
+        }
         return true;
     }
 
     /* Events from ScaleGestureDetector */
-    
+
+    @Override
     public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
         // Scale uses a focusX and focusY instead of x and y.  Focus is the middle
         // of the fingers.  Therefore, use the x and y values from the Down event
         // so that the x and y values don't jump to the middle position.
         // return true or all gestures for this beginning event will be discarded
-        logger.log(Level.INFO, "onScaleBegin");
+//        logger.log(Level.INFO, "onScaleBegin");
         scaleStartX = gestureDownX;
         scaleStartY = gestureDownY;
-        TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+        TouchEvent touchEvent = touchInput.getFreeTouchEvent();
         touchEvent.set(TouchEvent.Type.SCALE_START, scaleStartX, scaleStartY, 0f, 0f);
         touchEvent.setPointerId(0);
         touchEvent.setTime(scaleGestureDetector.getEventTime());
         touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
         touchEvent.setDeltaScaleSpan(0f);
         touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
-        touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress());
-        processEvent(touchEvent);
-        
+        touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
+        touchInput.addEvent(touchEvent);
+
         return true;
     }
 
+    @Override
     public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
         // return true or all gestures for this event will be accumulated
-        logger.log(Level.INFO, "onScale");
+//        logger.log(Level.INFO, "onScale");
         scaleStartX = gestureDownX;
         scaleStartY = gestureDownY;
-        TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+        TouchEvent touchEvent = touchInput.getFreeTouchEvent();
         touchEvent.set(TouchEvent.Type.SCALE_MOVE, scaleStartX, scaleStartY, 0f, 0f);
         touchEvent.setPointerId(0);
         touchEvent.setTime(scaleGestureDetector.getEventTime());
         touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
         touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
         touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
-        touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress());
-        processEvent(touchEvent);
+        touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
+        touchInput.addEvent(touchEvent);
         return true;
     }
 
+    @Override
     public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
-        logger.log(Level.INFO, "onScaleEnd");
+//        logger.log(Level.INFO, "onScaleEnd");
         scaleStartX = gestureDownX;
         scaleStartY = gestureDownY;
-        TouchEvent touchEvent = androidInput.getFreeTouchEvent();
+        TouchEvent touchEvent = touchInput.getFreeTouchEvent();
         touchEvent.set(TouchEvent.Type.SCALE_END, scaleStartX, scaleStartY, 0f, 0f);
         touchEvent.setPointerId(0);
         touchEvent.setTime(scaleGestureDetector.getEventTime());
         touchEvent.setScaleSpan(scaleGestureDetector.getCurrentSpan());
         touchEvent.setDeltaScaleSpan(scaleGestureDetector.getCurrentSpan() - scaleGestureDetector.getPreviousSpan());
         touchEvent.setScaleFactor(scaleGestureDetector.getScaleFactor());
-        touchEvent.setScaleSpanInProgress(scaleDetector.isInProgress());
-        processEvent(touchEvent);
+        touchEvent.setScaleSpanInProgress(touchInput.getScaleDetector().isInProgress());
+        touchInput.addEvent(touchEvent);
     }
 }

+ 0 - 686
jme3-android/src/main/java/com/jme3/input/android/AndroidInput.java

@@ -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;
-    }
-
-}

+ 147 - 172
jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java

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

+ 158 - 0
jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java

@@ -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;
+
+    }
+
+}

+ 20 - 42
jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInputHandler.java → jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput.java

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

+ 108 - 0
jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput14.java

@@ -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);
+    }
+
+}

+ 403 - 0
jme3-android/src/main/java/com/jme3/input/android/AndroidJoystickJoyInput14.java

@@ -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();
+        }
+    }
+}

+ 0 - 140
jme3-android/src/main/java/com/jme3/input/android/AndroidKeyHandler.java

@@ -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;
-        }
-
-   }
-
-}

+ 11 - 6
jme3-android/src/main/java/com/jme3/input/android/AndroidKeyMapping.java

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

+ 24 - 24
jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java

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

+ 0 - 257
jme3-android/src/main/java/com/jme3/input/android/AndroidTouchHandler.java

@@ -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;
-    }
-    
-}

+ 475 - 0
jme3-android/src/main/java/com/jme3/input/android/AndroidTouchInput.java

@@ -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;
+    }
+
+}

+ 30 - 46
jme3-android/src/main/java/com/jme3/input/android/AndroidTouchHandler14.java → jme3-android/src/main/java/com/jme3/input/android/AndroidTouchInput14.java

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

+ 9 - 14
jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java

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