• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.jme3.input.android;
2 
3 import android.content.Context;
4 import android.opengl.GLSurfaceView;
5 import android.util.AttributeSet;
6 import android.view.GestureDetector;
7 import android.view.KeyEvent;
8 import android.view.MotionEvent;
9 import android.view.ScaleGestureDetector;
10 import com.jme3.input.KeyInput;
11 import com.jme3.input.RawInputListener;
12 import com.jme3.input.TouchInput;
13 import com.jme3.input.event.MouseButtonEvent;
14 import com.jme3.input.event.MouseMotionEvent;
15 import com.jme3.input.event.TouchEvent;
16 import com.jme3.input.event.TouchEvent.Type;
17 import com.jme3.math.Vector2f;
18 import com.jme3.util.RingBuffer;
19 import java.util.HashMap;
20 import java.util.logging.Logger;
21 
22 /**
23  * <code>AndroidInput</code> is one of the main components that connect jme with android. Is derived from GLSurfaceView and handles all Inputs
24  * @author larynx
25  *
26  */
27 public class AndroidInput extends GLSurfaceView implements
28         TouchInput,
29         GestureDetector.OnGestureListener,
30         GestureDetector.OnDoubleTapListener,
31         ScaleGestureDetector.OnScaleGestureListener {
32 
33     final private static int MAX_EVENTS = 1024;
34     // Custom settings
35     public boolean mouseEventsEnabled = true;
36     public boolean mouseEventsInvertX = false;
37     public boolean mouseEventsInvertY = false;
38     public boolean keyboardEventsEnabled = false;
39     public boolean dontSendHistory = false;
40     // Used to transfer events from android thread to GLThread
41     final private RingBuffer<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS);
42     final private RingBuffer<TouchEvent> eventPoolUnConsumed = new RingBuffer<TouchEvent>(MAX_EVENTS);
43     final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS);
44     final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>();
45     // Internal
46     private ScaleGestureDetector scaledetector;
47     private GestureDetector detector;
48     private int lastX;
49     private int lastY;
50     private final static Logger logger = Logger.getLogger(AndroidInput.class.getName());
51     private boolean isInitialized = false;
52     private RawInputListener listener = null;
53     private static final int[] ANDROID_TO_JME = {
54         0x0, // unknown
55         0x0, // key code soft left
56         0x0, // key code soft right
57         KeyInput.KEY_HOME,
58         KeyInput.KEY_ESCAPE, // key back
59         0x0, // key call
60         0x0, // key endcall
61         KeyInput.KEY_0,
62         KeyInput.KEY_1,
63         KeyInput.KEY_2,
64         KeyInput.KEY_3,
65         KeyInput.KEY_4,
66         KeyInput.KEY_5,
67         KeyInput.KEY_6,
68         KeyInput.KEY_7,
69         KeyInput.KEY_8,
70         KeyInput.KEY_9,
71         KeyInput.KEY_MULTIPLY,
72         0x0, // key pound
73         KeyInput.KEY_UP,
74         KeyInput.KEY_DOWN,
75         KeyInput.KEY_LEFT,
76         KeyInput.KEY_RIGHT,
77         KeyInput.KEY_RETURN, // dpad center
78         0x0, // volume up
79         0x0, // volume down
80         KeyInput.KEY_POWER, // power (?)
81         0x0, // camera
82         0x0, // clear
83         KeyInput.KEY_A,
84         KeyInput.KEY_B,
85         KeyInput.KEY_C,
86         KeyInput.KEY_D,
87         KeyInput.KEY_E,
88         KeyInput.KEY_F,
89         KeyInput.KEY_G,
90         KeyInput.KEY_H,
91         KeyInput.KEY_I,
92         KeyInput.KEY_J,
93         KeyInput.KEY_K,
94         KeyInput.KEY_L,
95         KeyInput.KEY_M,
96         KeyInput.KEY_N,
97         KeyInput.KEY_O,
98         KeyInput.KEY_P,
99         KeyInput.KEY_Q,
100         KeyInput.KEY_R,
101         KeyInput.KEY_S,
102         KeyInput.KEY_T,
103         KeyInput.KEY_U,
104         KeyInput.KEY_V,
105         KeyInput.KEY_W,
106         KeyInput.KEY_X,
107         KeyInput.KEY_Y,
108         KeyInput.KEY_Z,
109         KeyInput.KEY_COMMA,
110         KeyInput.KEY_PERIOD,
111         KeyInput.KEY_LMENU,
112         KeyInput.KEY_RMENU,
113         KeyInput.KEY_LSHIFT,
114         KeyInput.KEY_RSHIFT,
115         //        0x0, // fn
116         //        0x0, // cap (?)
117 
118         KeyInput.KEY_TAB,
119         KeyInput.KEY_SPACE,
120         0x0, // sym (?) symbol
121         0x0, // explorer
122         0x0, // envelope
123         KeyInput.KEY_RETURN, // newline/enter
124         KeyInput.KEY_DELETE,
125         KeyInput.KEY_GRAVE,
126         KeyInput.KEY_MINUS,
127         KeyInput.KEY_EQUALS,
128         KeyInput.KEY_LBRACKET,
129         KeyInput.KEY_RBRACKET,
130         KeyInput.KEY_BACKSLASH,
131         KeyInput.KEY_SEMICOLON,
132         KeyInput.KEY_APOSTROPHE,
133         KeyInput.KEY_SLASH,
134         KeyInput.KEY_AT, // at (@)
135         KeyInput.KEY_NUMLOCK, //0x0, // num
136         0x0, //headset hook
137         0x0, //focus
138         KeyInput.KEY_ADD,
139         KeyInput.KEY_LMETA, //menu
140         0x0,//notification
141         0x0,//search
142         0x0,//media play/pause
143         0x0,//media stop
144         0x0,//media next
145         0x0,//media previous
146         0x0,//media rewind
147         0x0,//media fastforward
148         0x0,//mute
149     };
150 
AndroidInput(Context ctx, AttributeSet attribs)151     public AndroidInput(Context ctx, AttributeSet attribs) {
152         super(ctx, attribs);
153         detector = new GestureDetector(null, this, null, false);
154         scaledetector = new ScaleGestureDetector(ctx, this);
155 
156     }
157 
AndroidInput(Context ctx)158     public AndroidInput(Context ctx) {
159         super(ctx);
160         detector = new GestureDetector(null, this, null, false);
161         scaledetector = new ScaleGestureDetector(ctx, this);
162     }
163 
getNextFreeTouchEvent()164     private TouchEvent getNextFreeTouchEvent() {
165         return getNextFreeTouchEvent(false);
166     }
167 
168     /**
169      * Fetches a touch event from the reuse pool
170      * @param wait if true waits for a reusable event to get available/released
171      * by an other thread, if false returns a new one if needed.
172      *
173      * @return a usable TouchEvent
174      */
getNextFreeTouchEvent(boolean wait)175     private TouchEvent getNextFreeTouchEvent(boolean wait) {
176         TouchEvent evt = null;
177         synchronized (eventPoolUnConsumed) {
178             int size = eventPoolUnConsumed.size();
179             while (size > 0) {
180                 evt = eventPoolUnConsumed.pop();
181                 if (!evt.isConsumed()) {
182                     eventPoolUnConsumed.push(evt);
183                     evt = null;
184                 } else {
185                     break;
186                 }
187                 size--;
188             }
189         }
190 
191         if (evt == null) {
192             if (eventPool.isEmpty() && wait) {
193                 logger.warning("eventPool buffer underrun");
194                 boolean isEmpty;
195                 do {
196                     synchronized (eventPool) {
197                         isEmpty = eventPool.isEmpty();
198                     }
199                     try {
200                         Thread.sleep(50);
201                     } catch (InterruptedException e) {
202                     }
203                 } while (isEmpty);
204                 synchronized (eventPool) {
205                     evt = eventPool.pop();
206                 }
207             } else if (eventPool.isEmpty()) {
208                 evt = new TouchEvent();
209                 logger.warning("eventPool buffer underrun");
210             } else {
211                 synchronized (eventPool) {
212                     evt = eventPool.pop();
213                 }
214             }
215         }
216         return evt;
217     }
218 
219     /**
220      * onTouchEvent gets called from android thread on touchpad events
221      */
222     @Override
onTouchEvent(MotionEvent event)223     public boolean onTouchEvent(MotionEvent event) {
224         boolean bWasHandled = false;
225         TouchEvent touch;
226         //    System.out.println("native : " + event.getAction());
227         int action = event.getAction() & MotionEvent.ACTION_MASK;
228         int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
229                 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
230         int pointerId = event.getPointerId(pointerIndex);
231 
232         // final int historySize = event.getHistorySize();
233         //final int pointerCount = event.getPointerCount();
234 
235         switch (action) {
236             case MotionEvent.ACTION_POINTER_DOWN:
237             case MotionEvent.ACTION_DOWN:
238                 touch = getNextFreeTouchEvent();
239                 touch.set(Type.DOWN, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);
240                 touch.setPointerId(pointerId);
241                 touch.setTime(event.getEventTime());
242                 touch.setPressure(event.getPressure(pointerIndex));
243                 processEvent(touch);
244 
245                 bWasHandled = true;
246                 break;
247 
248             case MotionEvent.ACTION_POINTER_UP:
249             case MotionEvent.ACTION_CANCEL:
250             case MotionEvent.ACTION_UP:
251 
252                 touch = getNextFreeTouchEvent();
253                 touch.set(Type.UP, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);
254                 touch.setPointerId(pointerId);
255                 touch.setTime(event.getEventTime());
256                 touch.setPressure(event.getPressure(pointerIndex));
257                 processEvent(touch);
258 
259 
260                 bWasHandled = true;
261                 break;
262             case MotionEvent.ACTION_MOVE:
263 
264 
265                 // Convert all pointers into events
266                 for (int p = 0; p < event.getPointerCount(); p++) {
267                     Vector2f lastPos = lastPositions.get(pointerIndex);
268                     if (lastPos == null) {
269                         lastPos = new Vector2f(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));
270                         lastPositions.put(pointerId, lastPos);
271                     }
272                     touch = getNextFreeTouchEvent();
273                     touch.set(Type.MOVE, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), event.getX(pointerIndex) - lastPos.x, this.getHeight() - event.getY(pointerIndex) - lastPos.y);
274                     touch.setPointerId(pointerId);
275                     touch.setTime(event.getEventTime());
276                     touch.setPressure(event.getPressure(pointerIndex));
277                     processEvent(touch);
278                     lastPos.set(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));
279                 }
280                 bWasHandled = true;
281                 break;
282             case MotionEvent.ACTION_OUTSIDE:
283                 break;
284 
285         }
286 
287         // Try to detect gestures
288         this.detector.onTouchEvent(event);
289         this.scaledetector.onTouchEvent(event);
290 
291         return bWasHandled;
292     }
293 
294     @Override
onKeyDown(int keyCode, KeyEvent event)295     public boolean onKeyDown(int keyCode, KeyEvent event) {
296         TouchEvent evt;
297         evt = getNextFreeTouchEvent();
298         evt.set(TouchEvent.Type.KEY_DOWN);
299         evt.setKeyCode(keyCode);
300         evt.setCharacters(event.getCharacters());
301         evt.setTime(event.getEventTime());
302 
303         // Send the event
304         processEvent(evt);
305 
306         // Handle all keys ourself except Volume Up/Down
307         if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
308             return false;
309         } else {
310             return true;
311         }
312     }
313 
314     @Override
onKeyUp(int keyCode, KeyEvent event)315     public boolean onKeyUp(int keyCode, KeyEvent event) {
316         TouchEvent evt;
317         evt = getNextFreeTouchEvent();
318         evt.set(TouchEvent.Type.KEY_UP);
319         evt.setKeyCode(keyCode);
320         evt.setCharacters(event.getCharacters());
321         evt.setTime(event.getEventTime());
322 
323         // Send the event
324         processEvent(evt);
325 
326         // Handle all keys ourself except Volume Up/Down
327         if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
328             return false;
329         } else {
330             return true;
331         }
332     }
333 
334     // -----------------------------------------
335     // JME3 Input interface
336     @Override
initialize()337     public void initialize() {
338         TouchEvent item;
339         for (int i = 0; i < MAX_EVENTS; i++) {
340             item = new TouchEvent();
341             eventPool.push(item);
342         }
343         isInitialized = true;
344     }
345 
346     @Override
destroy()347     public void destroy() {
348         isInitialized = false;
349 
350         // Clean up queues
351         while (!eventPool.isEmpty()) {
352             eventPool.pop();
353         }
354         while (!eventQueue.isEmpty()) {
355             eventQueue.pop();
356         }
357     }
358 
359     @Override
isInitialized()360     public boolean isInitialized() {
361         return isInitialized;
362     }
363 
364     @Override
setInputListener(RawInputListener listener)365     public void setInputListener(RawInputListener listener) {
366         this.listener = listener;
367     }
368 
369     @Override
getInputTimeNanos()370     public long getInputTimeNanos() {
371         return System.nanoTime();
372     }
373     // -----------------------------------------
374 
processEvent(TouchEvent event)375     private void processEvent(TouchEvent event) {
376         synchronized (eventQueue) {
377             eventQueue.push(event);
378         }
379     }
380 
381     //  ---------------  INSIDE GLThread  ---------------
382     @Override
update()383     public void update() {
384         generateEvents();
385     }
386 
generateEvents()387     private void generateEvents() {
388         if (listener != null) {
389             TouchEvent event;
390             MouseButtonEvent btn;
391             int newX;
392             int newY;
393 
394             while (!eventQueue.isEmpty()) {
395                 synchronized (eventQueue) {
396                     event = eventQueue.pop();
397                 }
398                 if (event != null) {
399                     listener.onTouchEvent(event);
400 
401                     if (mouseEventsEnabled) {
402                         if (mouseEventsInvertX) {
403                             newX = this.getWidth() - (int) event.getX();
404                         } else {
405                             newX = (int) event.getX();
406                         }
407 
408                         if (mouseEventsInvertY) {
409                             newY = this.getHeight() - (int) event.getY();
410                         } else {
411                             newY = (int) event.getY();
412                         }
413 
414                         switch (event.getType()) {
415                             case DOWN:
416                                 // Handle mouse down event
417                                 btn = new MouseButtonEvent(0, true, newX, newY);
418                                 btn.setTime(event.getTime());
419                                 listener.onMouseButtonEvent(btn);
420                                 // Store current pos
421                                 lastX = -1;
422                                 lastY = -1;
423                                 break;
424 
425                             case UP:
426                                 // Handle mouse up event
427                                 btn = new MouseButtonEvent(0, false, newX, newY);
428                                 btn.setTime(event.getTime());
429                                 listener.onMouseButtonEvent(btn);
430                                 // Store current pos
431                                 lastX = -1;
432                                 lastY = -1;
433                                 break;
434 
435                             case MOVE:
436                                 int dx;
437                                 int dy;
438                                 if (lastX != -1) {
439                                     dx = newX - lastX;
440                                     dy = newY - lastY;
441                                 } else {
442                                     dx = 0;
443                                     dy = 0;
444                                 }
445                                 MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0);
446                                 mot.setTime(event.getTime());
447                                 listener.onMouseMotionEvent(mot);
448                                 lastX = newX;
449                                 lastY = newY;
450                                 break;
451                         }
452 
453 
454                     }
455                 }
456 
457                 if (event.isConsumed() == false) {
458                     synchronized (eventPoolUnConsumed) {
459                         eventPoolUnConsumed.push(event);
460                     }
461 
462                 } else {
463                     synchronized (eventPool) {
464                         eventPool.push(event);
465                     }
466                 }
467             }
468 
469         }
470     }
471     //  --------------- ENDOF INSIDE GLThread  ---------------
472 
473     // --------------- Gesture detected callback events  ---------------
onDown(MotionEvent event)474     public boolean onDown(MotionEvent event) {
475         return false;
476     }
477 
onLongPress(MotionEvent event)478     public void onLongPress(MotionEvent event) {
479         TouchEvent touch = getNextFreeTouchEvent();
480         touch.set(Type.LONGPRESSED, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
481         touch.setPointerId(0);
482         touch.setTime(event.getEventTime());
483         processEvent(touch);
484     }
485 
onFling(MotionEvent event, MotionEvent event2, float vx, float vy)486     public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) {
487         TouchEvent touch = getNextFreeTouchEvent();
488         touch.set(Type.FLING, event.getX(), this.getHeight() - event.getY(), vx, vy);
489         touch.setPointerId(0);
490         touch.setTime(event.getEventTime());
491         processEvent(touch);
492 
493         return true;
494     }
495 
onSingleTapConfirmed(MotionEvent event)496     public boolean onSingleTapConfirmed(MotionEvent event) {
497         //Nothing to do here the tap has already been detected.
498         return false;
499     }
500 
onDoubleTap(MotionEvent event)501     public boolean onDoubleTap(MotionEvent event) {
502         TouchEvent touch = getNextFreeTouchEvent();
503         touch.set(Type.DOUBLETAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
504         touch.setPointerId(0);
505         touch.setTime(event.getEventTime());
506         processEvent(touch);
507         return true;
508     }
509 
onDoubleTapEvent(MotionEvent event)510     public boolean onDoubleTapEvent(MotionEvent event) {
511         return false;
512     }
513 
onScaleBegin(ScaleGestureDetector scaleGestureDetector)514     public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
515         TouchEvent touch = getNextFreeTouchEvent();
516         touch.set(Type.SCALE_START, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f);
517         touch.setPointerId(0);
518         touch.setTime(scaleGestureDetector.getEventTime());
519         touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
520         touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
521         processEvent(touch);
522         //    System.out.println("scaleBegin");
523 
524         return true;
525     }
526 
onScale(ScaleGestureDetector scaleGestureDetector)527     public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
528         TouchEvent touch = getNextFreeTouchEvent();
529         touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
530         touch.setPointerId(0);
531         touch.setTime(scaleGestureDetector.getEventTime());
532         touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
533         touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
534         processEvent(touch);
535         //   System.out.println("scale");
536 
537         return false;
538     }
539 
onScaleEnd(ScaleGestureDetector scaleGestureDetector)540     public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
541         TouchEvent touch = getNextFreeTouchEvent();
542         touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f);
543         touch.setPointerId(0);
544         touch.setTime(scaleGestureDetector.getEventTime());
545         touch.setScaleSpan(scaleGestureDetector.getCurrentSpan());
546         touch.setScaleFactor(scaleGestureDetector.getScaleFactor());
547         processEvent(touch);
548     }
549 
onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)550     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
551         TouchEvent touch = getNextFreeTouchEvent();
552         touch.set(Type.SCROLL, e1.getX(), this.getHeight() - e1.getY(), distanceX, distanceY * (-1));
553         touch.setPointerId(0);
554         touch.setTime(e1.getEventTime());
555         processEvent(touch);
556         //System.out.println("scroll " + e1.getPointerCount());
557         return false;
558     }
559 
onShowPress(MotionEvent event)560     public void onShowPress(MotionEvent event) {
561         TouchEvent touch = getNextFreeTouchEvent();
562         touch.set(Type.SHOWPRESS, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
563         touch.setPointerId(0);
564         touch.setTime(event.getEventTime());
565         processEvent(touch);
566     }
567 
onSingleTapUp(MotionEvent event)568     public boolean onSingleTapUp(MotionEvent event) {
569         TouchEvent touch = getNextFreeTouchEvent();
570         touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f);
571         touch.setPointerId(0);
572         touch.setTime(event.getEventTime());
573         processEvent(touch);
574         return true;
575     }
576 
577     @Override
setSimulateMouse(boolean simulate)578     public void setSimulateMouse(boolean simulate) {
579         mouseEventsEnabled = simulate;
580     }
581     @Override
getSimulateMouse()582     public boolean getSimulateMouse() {
583         return mouseEventsEnabled;
584     }
585 
586     @Override
setSimulateKeyboard(boolean simulate)587     public void setSimulateKeyboard(boolean simulate) {
588         keyboardEventsEnabled = simulate;
589     }
590 
591     @Override
setOmitHistoricEvents(boolean dontSendHistory)592     public void setOmitHistoricEvents(boolean dontSendHistory) {
593         this.dontSendHistory = dontSendHistory;
594     }
595 
596     // TODO: move to TouchInput
isMouseEventsEnabled()597     public boolean isMouseEventsEnabled() {
598         return mouseEventsEnabled;
599     }
600 
setMouseEventsEnabled(boolean mouseEventsEnabled)601     public void setMouseEventsEnabled(boolean mouseEventsEnabled) {
602         this.mouseEventsEnabled = mouseEventsEnabled;
603     }
604 
isMouseEventsInvertY()605     public boolean isMouseEventsInvertY() {
606         return mouseEventsInvertY;
607     }
608 
setMouseEventsInvertY(boolean mouseEventsInvertY)609     public void setMouseEventsInvertY(boolean mouseEventsInvertY) {
610         this.mouseEventsInvertY = mouseEventsInvertY;
611     }
612 
isMouseEventsInvertX()613     public boolean isMouseEventsInvertX() {
614         return mouseEventsInvertX;
615     }
616 
setMouseEventsInvertX(boolean mouseEventsInvertX)617     public void setMouseEventsInvertX(boolean mouseEventsInvertX) {
618         this.mouseEventsInvertX = mouseEventsInvertX;
619     }
620 }
621