diff --git a/engine/src/android/com/jme3/app/AndroidHarness.java b/engine/src/android/com/jme3/app/AndroidHarness.java index 5fb82f389..44b9b0588 100644 --- a/engine/src/android/com/jme3/app/AndroidHarness.java +++ b/engine/src/android/com/jme3/app/AndroidHarness.java @@ -11,7 +11,12 @@ import android.view.Window; import android.view.WindowManager; import com.jme3.app.Application; +import com.jme3.app.android.AndroidApplication; +import com.jme3.input.TouchInput; import com.jme3.input.android.AndroidInput; +import com.jme3.input.controls.TouchListener; +import com.jme3.input.controls.TouchTrigger; +import com.jme3.input.event.TouchEvent; import com.jme3.system.AppSettings; import com.jme3.system.JmeSystem; import com.jme3.system.android.OGLESContext; @@ -22,7 +27,7 @@ import com.jme3.system.android.OGLESContext; * @author Kirill * @author larynx */ -public class AndroidHarness extends Activity implements DialogInterface.OnClickListener +public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener { protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName()); @@ -33,6 +38,8 @@ public class AndroidHarness extends Activity implements DialogInterface.OnClickL protected Application app = null; protected boolean debug = false; + + final private String ESCAPE_EVENT = "TouchEscape"; @Override public void onCreate(Bundle savedInstanceState) @@ -70,7 +77,10 @@ public class AndroidHarness extends Activity implements DialogInterface.OnClickL { view = ctx.createView(input); } - setContentView(view); + setContentView(view); + + app.inputManager.addMapping(ESCAPE_EVENT, new TouchTrigger(TouchInput.KEYCODE_BACK)); + app.inputManager.addListener(this, new String[]{ESCAPE_EVENT}); } @@ -154,7 +164,6 @@ public class AndroidHarness extends Activity implements DialogInterface.OnClickL }); } - /** * Called by the android alert dialog, terminate the activity and OpenGL rendering @@ -162,8 +171,49 @@ public class AndroidHarness extends Activity implements DialogInterface.OnClickL * @param whichButton */ public void onClick(DialogInterface dialog, int whichButton) - { - app.stop(); - this.finish(); + { + if (whichButton != -2) + { + app.stop(); + this.finish(); + } } + + /** + * Gets called by the InputManager on all touch/drag/scale events + */ + @Override + public void onTouch(String name, TouchEvent evt, float tpf) + { + if (name.equals(ESCAPE_EVENT)) + { + switch(evt.getType()) + { + case KEY_UP: + this.runOnUiThread(new Runnable() + { + @Override + public void run() + { + AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) + // .setIcon(R.drawable.alert_dialog_icon) + .setTitle("Do you want to exit?") + .setPositiveButton("Yes", AndroidHarness.this) + .setNegativeButton("No", AndroidHarness.this) + .setMessage("Use your home key to bring this app into the background or exit to terminate it.") + .create(); + dialog.show(); + } + }); + + + break; + + default: + break; + } + } + + } + } diff --git a/engine/src/android/com/jme3/app/android/AndroidApplication.java b/engine/src/android/com/jme3/app/android/AndroidApplication.java index 5d6ed2c75..19268eb83 100644 --- a/engine/src/android/com/jme3/app/android/AndroidApplication.java +++ b/engine/src/android/com/jme3/app/android/AndroidApplication.java @@ -41,7 +41,11 @@ import android.content.DialogInterface; import com.jme3.app.Application; import com.jme3.font.BitmapFont; import com.jme3.font.BitmapText; +import com.jme3.input.TouchInput; import com.jme3.input.android.AndroidInput; +import com.jme3.input.controls.TouchListener; +import com.jme3.input.controls.TouchTrigger; +import com.jme3.input.event.TouchEvent; import com.jme3.renderer.RenderManager; import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.scene.Node; @@ -60,8 +64,8 @@ import com.jme3.app.AndroidHarness; * * @deprecated Please use {@link AndroidHarness} instead. */ - @Deprecated -public abstract class AndroidApplication extends Application implements DialogInterface.OnClickListener +@Deprecated +public abstract class AndroidApplication extends Application implements DialogInterface.OnClickListener, TouchListener { protected final static Logger logger = Logger.getLogger(AndroidApplication.class.getName()); @@ -156,6 +160,9 @@ public abstract class AndroidApplication extends Application implements DialogIn loadFPSText(); viewPort.attachScene(rootNode); guiViewPort.attachScene(guiNode); + + inputManager.addMapping("TouchEscape", new TouchTrigger(TouchInput.KEYCODE_BACK)); + inputManager.addListener(this, new String[]{"TouchEscape"}); // call user code init(); @@ -286,9 +293,47 @@ public abstract class AndroidApplication extends Application implements DialogIn * @param whichButton */ public void onClick(DialogInterface dialog, int whichButton) - { - this.stop(); - activity.finish(); + { + if (whichButton != -2) + { + this.stop(); + activity.finish(); + } } + + /** + * Gets called by the InputManager on all touch/drag/scale events + */ + @Override + public void onTouch(String name, TouchEvent evt, float tpf) + { + switch(evt.getType()) + { + + case KEY_UP: + activity.runOnUiThread(new Runnable() + { + @Override + public void run() + { + AlertDialog dialog = new AlertDialog.Builder(activity) + // .setIcon(R.drawable.alert_dialog_icon) + .setTitle("Do you want to exit?") + .setPositiveButton("Yes", AndroidApplication.this) + .setNegativeButton("No", AndroidApplication.this) + .setMessage("Use your home key to bring this app into the background or Exit to terminate it.") + .create(); + dialog.show(); + } + }); + + + break; + + default: + break; + } + + } } diff --git a/engine/src/android/com/jme3/input/android/AndroidInput.java b/engine/src/android/com/jme3/input/android/AndroidInput.java index 51e54efdf..71555caee 100644 --- a/engine/src/android/com/jme3/input/android/AndroidInput.java +++ b/engine/src/android/com/jme3/input/android/AndroidInput.java @@ -35,11 +35,13 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, final private static int MAX_EVENTS = 1024; final private RingBuffer eventQueue = new RingBuffer(MAX_EVENTS); + final private RingBuffer eventPoolUnConsumed = new RingBuffer(MAX_EVENTS); final private RingBuffer eventPool = new RingBuffer(MAX_EVENTS); final private HashMap lastPositions = new HashMap(); public boolean fireMouseEvents = true; public boolean fireKeyboardEvents = false; + public boolean dontSendHistory = false; private ScaleGestureDetector scaledetector; private GestureDetector detector; @@ -169,33 +171,67 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, return getNextFreeTouchEvent(false); } + /** + * Fetches a touch event from the reuse pool + * @param wait + * @return + */ private TouchEvent getNextFreeTouchEvent(boolean wait) { - TouchEvent evt; - if (eventPool.isEmpty() && wait) + TouchEvent evt = null; + synchronized(eventPoolUnConsumed) { - logger.warning("eventPool buffer underrun"); - boolean isEmpty; - do + int size = eventPoolUnConsumed.size(); + while (size > 0) { - synchronized(eventPool) + evt = eventPoolUnConsumed.pop(); + if (!evt.isConsumed()) { - isEmpty = eventPool.isEmpty(); + eventPoolUnConsumed.push(evt); + evt = null; } - try { Thread.sleep(50); } catch (InterruptedException e) { } + else + { + break; + } + size--; } - while (isEmpty); - evt = eventPool.pop(); } - else if (eventPool.isEmpty()) + + + if (evt == null) { - evt = new TouchEvent(); - logger.warning("eventPool buffer underrun"); + 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(); + } + } } - else - { - evt = eventPool.pop(); - } return evt; } /** @@ -210,17 +246,37 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, // Try to detect gestures this.detector.onTouchEvent(event); this.scaledetector.onTouchEvent(event); - + + final int historySize = event.getHistorySize(); + final int pointerCount = event.getPointerCount(); switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - + case MotionEvent.ACTION_DOWN: + + if (!dontSendHistory) + { + // Process history + for (int h = 0; h < historySize; h++) + { + // Convert all pointers into events + for (int p = 0; p < pointerCount; p++) + { + touch = getNextFreeTouchEvent(); + touch.set(Type.DOWN, event.getHistoricalX(p, h), this.getHeight() - event.getHistoricalY(p, h), 0, 0); + touch.setPointerId(event.getPointerId(p)); + touch.setTime(event.getHistoricalEventTime(h)); + touch.setPressure(event.getHistoricalPressure(p, h)); + processEvent(touch); + } + + } + } // Convert all pointers into events - for (int p = 0; p < event.getPointerCount(); p++) + for (int p = 0; p < pointerCount; p++) { touch = getNextFreeTouchEvent(); - touch.set(Type.DOWN, event.getX(p), event.getY(p), 0, 0); + touch.set(Type.DOWN, event.getX(p), this.getHeight() - event.getY(p), 0, 0); touch.setPointerId(event.getPointerId(p)); touch.setTime(event.getEventTime()); touch.setPressure(event.getPressure(p)); @@ -232,11 +288,30 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, case MotionEvent.ACTION_UP: + if (!dontSendHistory) + { + // Process history + for (int h = 0; h < historySize; h++) + { + // Convert all pointers into events + for (int p = 0; p < pointerCount; p++) + { + touch = getNextFreeTouchEvent(); + touch.set(Type.UP, event.getHistoricalX(p, h), this.getHeight() - event.getHistoricalY(p, h), 0, 0); + touch.setPointerId(event.getPointerId(p)); + touch.setTime(event.getHistoricalEventTime(h)); + touch.setPressure(event.getHistoricalPressure(p, h)); + processEvent(touch); + } + + } + } + // Convert all pointers into events - for (int p = 0; p < event.getPointerCount(); p++) + for (int p = 0; p < pointerCount; p++) { touch = getNextFreeTouchEvent(); - touch.set(Type.UP, event.getX(p), event.getY(p), 0, 0); + touch.set(Type.UP, event.getX(p), this.getHeight() - event.getY(p), 0, 0); touch.setPointerId(event.getPointerId(p)); touch.setTime(event.getEventTime()); touch.setPressure(event.getPressure(p)); @@ -247,22 +322,50 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, break; case MotionEvent.ACTION_MOVE: + if (!dontSendHistory) + { + // Process history + for (int h = 0; h < historySize; h++) + { + // Convert all pointers into events + for (int p = 0; p < pointerCount; p++) + { + Vector2f lastPos = lastPositions.get(event.getPointerId(p)); + if (lastPos == null) + { + lastPos = new Vector2f(event.getHistoricalX(p,h ), this.getHeight() - event.getHistoricalY(p, h)); + lastPositions.put(event.getPointerId(p), lastPos); + } + + touch = getNextFreeTouchEvent(); + touch.set(Type.MOVE, event.getHistoricalX(p, h), this.getHeight() - event.getHistoricalY(p, h), + event.getHistoricalX(p, h) - lastPos.x, this.getHeight() - event.getHistoricalY(p, h) - lastPos.y); + touch.setPointerId(event.getPointerId(p)); + touch.setTime(event.getHistoricalEventTime(h)); + touch.setPressure(event.getHistoricalPressure(p, h)); + processEvent(touch); + lastPos.set(event.getHistoricalX(p, h), this.getHeight() - event.getHistoricalY(p, h)); + } + + } + } + // Convert all pointers into events for (int p = 0; p < event.getPointerCount(); p++) { Vector2f lastPos = lastPositions.get(event.getPointerId(p)); if (lastPos == null) { - lastPos = new Vector2f(event.getX(p), event.getY(p)); + lastPos = new Vector2f(event.getX(p), this.getHeight() - event.getY(p)); lastPositions.put(event.getPointerId(p), lastPos); } touch = getNextFreeTouchEvent(); - touch.set(Type.MOVE, event.getX(p), event.getY(p), event.getX(p) - lastPos.x, event.getY(p) - lastPos.y); + touch.set(Type.MOVE, event.getX(p), this.getHeight() - event.getY(p), event.getX(p) - lastPos.x, this.getHeight() - event.getY(p) - lastPos.y); touch.setPointerId(event.getPointerId(p)); touch.setTime(event.getEventTime()); touch.setPressure(event.getPressure(p)); processEvent(touch); - lastPos.set(event.getX(p), event.getY(p)); + lastPos.set(event.getX(p), this.getHeight() - event.getY(p)); } bWasHandled = true; break; @@ -296,11 +399,8 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, // Send the event processEvent(evt); - // Handle all keys ourself, except the back button (4) - if (keyCode == 4) - return false; - else - return true; + // Handle all keys ourself + return true; } @Override @@ -316,11 +416,8 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, // Send the event processEvent(evt); - // Handle all keys ourself, except the back button (4) - if (keyCode == 4) - return false; - else - return true; + // Handle all keys ourself + return true; } @@ -408,8 +505,8 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, if (fireMouseEvents) { - newX = getWidth() - (int) event.getX(); - newY = (int) event.getY(); + newX = this.getWidth() - (int) event.getX(); + newY = this.getHeight() - (int) event.getY(); switch (event.getType()) { case DOWN: @@ -451,10 +548,22 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, } } } - synchronized (eventPool) + + if (event.isConsumed() == false) { - eventPool.push(event); - } + synchronized (eventPoolUnConsumed) + { + eventPoolUnConsumed.push(event); + } + + } + else + { + synchronized (eventPool) + { + eventPool.push(event); + } + } } } @@ -470,7 +579,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, public void onLongPress(MotionEvent event) { TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.LONGPRESSED, event.getX(), event.getY(), 0f, 0f); + touch.set(Type.LONGPRESSED, event.getX(), this.getHeight() - event.getY(), 0f, 0f); touch.setPointerId(0); touch.setTime(event.getEventTime()); processEvent(touch); @@ -479,7 +588,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) { TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.FLING, event.getX(), event.getY(), vx, vy); + touch.set(Type.FLING, event.getX(), this.getHeight() - event.getY(), vx, vy); touch.setPointerId(0); touch.setTime(event.getEventTime()); processEvent(touch); @@ -490,7 +599,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, public boolean onSingleTapConfirmed(MotionEvent event) { TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.TAP, event.getX(), event.getY(), 0f, 0f); + touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f); touch.setPointerId(0); touch.setTime(event.getEventTime()); processEvent(touch); @@ -501,7 +610,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, public boolean onDoubleTap(MotionEvent event) { TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.DOUBLETAP, event.getX(), event.getY(), 0f, 0f); + touch.set(Type.DOUBLETAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f); touch.setPointerId(0); touch.setTime(event.getEventTime()); processEvent(touch); @@ -524,7 +633,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, public boolean onScale(ScaleGestureDetector scaleGestureDetector) { TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f); + touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f); touch.setPointerId(0); touch.setTime(scaleGestureDetector.getEventTime()); touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); @@ -537,7 +646,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) { TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f); + touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f); touch.setPointerId(0); touch.setTime(scaleGestureDetector.getEventTime()); touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); @@ -548,7 +657,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SCROLL, e1.getX(), e1.getY(), distanceX, distanceY); + touch.set(Type.SCROLL, e1.getX(), this.getHeight() - e1.getY(), distanceX, distanceY*(-1)); touch.setPointerId(0); touch.setTime(e1.getEventTime()); processEvent(touch); @@ -558,7 +667,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, public void onShowPress(MotionEvent event) { TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SHOWPRESS, event.getX(), event.getY(), 0f, 0f); + touch.set(Type.SHOWPRESS, event.getX(), this.getHeight() - event.getY(), 0f, 0f); touch.setPointerId(0); touch.setTime(event.getEventTime()); processEvent(touch); @@ -567,7 +676,7 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, public boolean onSingleTapUp(MotionEvent event) { TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.TAP, event.getX(), event.getY(), 0f, 0f); + touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f); touch.setPointerId(0); touch.setTime(event.getEventTime()); processEvent(touch); @@ -585,5 +694,10 @@ public class AndroidInput extends GLSurfaceView implements TouchInput, { fireKeyboardEvents = simulate; } + @Override + public void setOmitHistoricEvents(boolean dontSendHistory) + { + this.dontSendHistory = dontSendHistory; + } }