commit
013f67ee89
@ -0,0 +1,13 @@ |
||||
apply plugin: 'com.android.application' |
||||
|
||||
group = 'com.jme3' |
||||
version = jmeVersion + '-' + jmeVersionTag |
||||
|
||||
sourceCompatibility = '1.6' |
||||
|
||||
repositories { |
||||
mavenCentral() |
||||
maven { |
||||
url "http://nifty-gui.sourceforge.net/nifty-maven-repo" |
||||
} |
||||
} |
@ -0,0 +1,40 @@ |
||||
dependencies { |
||||
compile project(':jme3-core') |
||||
compile project(':jme3-android') |
||||
compile project(':jme3-effects') |
||||
compile project(':jme3-bullet') |
||||
compile project(':jme3-bullet-native-android') |
||||
compile project(':jme3-networking') |
||||
compile project(':jme3-niftygui') |
||||
compile project(':jme3-plugins') |
||||
compile project(':jme3-terrain') |
||||
compile project(':jme3-testdata') |
||||
} |
||||
|
||||
android { |
||||
compileSdkVersion 10 |
||||
buildToolsVersion "22.0.1" |
||||
|
||||
lintOptions { |
||||
// Fix nifty gui referencing "java.awt" package. |
||||
disable 'InvalidPackage' |
||||
} |
||||
|
||||
defaultConfig { |
||||
applicationId "com.jme3.android" |
||||
minSdkVersion 10 // Android 2.3 GINGERBREAD |
||||
targetSdkVersion 22 // Android 5.1 LOLLIPOP |
||||
versionCode 1 |
||||
versionName "1.0" // TODO: from settings.gradle |
||||
} |
||||
|
||||
buildTypes { |
||||
release { |
||||
minifyEnabled false |
||||
} |
||||
debug { |
||||
applicationIdSuffix ".debug" |
||||
debuggable true |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,20 @@ |
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||
package="com.jme3.android"> |
||||
|
||||
<!-- Tell the system that you need ES 2.0. --> |
||||
<uses-feature android:glEsVersion="0x00020000" android:required="true" /> |
||||
|
||||
<!-- Tell the system that you need distinct touches (for the zoom gesture). --> |
||||
<uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="true" /> |
||||
|
||||
<application android:label="@string/app_name" android:allowBackup="true"> |
||||
<activity android:name="jme3test.android.TestChooserAndroid" |
||||
android:label="@string/app_name"> |
||||
<intent-filter> |
||||
<action android:name="android.intent.action.MAIN" /> |
||||
<category android:name="android.intent.category.LAUNCHER" /> |
||||
</intent-filter> |
||||
</activity> |
||||
</application> |
||||
|
||||
</manifest> |
@ -0,0 +1,12 @@ |
||||
package jme3test.android; |
||||
|
||||
import android.content.pm.ActivityInfo; |
||||
import android.app.*; |
||||
import android.os.Bundle; |
||||
|
||||
public class TestChooserAndroid extends Activity { |
||||
@Override |
||||
public void onCreate(Bundle savedInstanceState) { |
||||
super.onCreate(savedInstanceState); |
||||
} |
||||
} |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
<string name="app_name">JMEAndroidTest</string> |
||||
<string name="about">About</string> |
||||
<string name="quit">Quit</string> |
||||
</resources> |
@ -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; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,159 @@ |
||||
/* |
||||
* 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});
|
||||
// use inclusive OR to make sure the onKey method is called.
|
||||
consumed = consumed | ((AndroidJoyInput14)joyInput).onKey(event); |
||||
} |
||||
|
||||
return consumed; |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,238 @@ |
||||
/* |
||||
* 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.content.Context; |
||||
import android.opengl.GLSurfaceView; |
||||
import android.os.Vibrator; |
||||
import com.jme3.input.InputManager; |
||||
import com.jme3.input.JoyInput; |
||||
import com.jme3.input.Joystick; |
||||
import com.jme3.input.RawInputListener; |
||||
import com.jme3.input.event.InputEvent; |
||||
import com.jme3.input.event.JoyAxisEvent; |
||||
import com.jme3.input.event.JoyButtonEvent; |
||||
import com.jme3.system.AppSettings; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.concurrent.ConcurrentLinkedQueue; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* Main class that manages various joystick devices. Joysticks can be many forms |
||||
* including a simulated joystick to communicate the device orientation as well |
||||
* as physical gamepads. </br> |
||||
* This class manages all the joysticks and feeds the inputs from each back |
||||
* to jME's InputManager. |
||||
* |
||||
* This handler also supports the joystick.rumble(rumbleAmount) method. In this |
||||
* case, when joystick.rumble(rumbleAmount) is called, the Android device will vibrate |
||||
* if the device has a built in vibrate motor. |
||||
* |
||||
* Because Andorid does not allow for the user to define the intensity of the |
||||
* vibration, the rumble amount (ie strength) is converted into vibration pulses |
||||
* The stronger the strength amount, the shorter the delay between pulses. If |
||||
* amount is 1, then the vibration stays on the whole time. If amount is 0.5, |
||||
* the vibration will a pulse of equal parts vibration and delay. |
||||
* To turn off vibration, set rumble amount to 0. |
||||
* |
||||
* MainActivity needs the following line to enable Joysticks on Android platforms |
||||
* joystickEventsEnabled = true; |
||||
* This is done to allow for battery conservation when sensor data or gamepads |
||||
* are not required by the application. |
||||
* |
||||
* To use the joystick rumble feature, the following line needs to be |
||||
* added to the Android Manifest File |
||||
* <uses-permission android:name="android.permission.VIBRATE"/> |
||||
* |
||||
* @author iwgeric |
||||
*/ |
||||
public class AndroidJoyInput implements JoyInput { |
||||
private static final Logger logger = Logger.getLogger(AndroidJoyInput.class.getName()); |
||||
public static boolean disableSensors = false; |
||||
|
||||
protected AndroidInputHandler inputHandler; |
||||
protected List<Joystick> joystickList = new ArrayList<Joystick>(); |
||||
// private boolean dontSendHistory = false;
|
||||
|
||||
|
||||
// Internal
|
||||
private boolean initialized = false; |
||||
private RawInputListener listener = null; |
||||
private ConcurrentLinkedQueue<InputEvent> eventQueue = new ConcurrentLinkedQueue<InputEvent>(); |
||||
private AndroidSensorJoyInput sensorJoyInput; |
||||
private Vibrator vibrator = null; |
||||
private boolean vibratorActive = false; |
||||
private long maxRumbleTime = 250; // 250ms
|
||||
|
||||
public AndroidJoyInput(AndroidInputHandler inputHandler) { |
||||
this.inputHandler = inputHandler; |
||||
sensorJoyInput = new AndroidSensorJoyInput(this); |
||||
} |
||||
|
||||
public void setView(GLSurfaceView 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); |
||||
} |
||||
} |
||||
|
||||
public void loadSettings(AppSettings settings) { |
||||
|
||||
} |
||||
|
||||
public void addEvent(InputEvent event) { |
||||
eventQueue.add(event); |
||||
} |
||||
|
||||
/** |
||||
* Pauses the joystick device listeners to save battery life if they are not needed. |
||||
* Used to pause when the activity pauses |
||||
*/ |
||||
public void pauseJoysticks() { |
||||
if (sensorJoyInput != null) { |
||||
sensorJoyInput.pauseSensors(); |
||||
} |
||||
if (vibrator != null && vibratorActive) { |
||||
vibrator.cancel(); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Resumes the joystick device listeners. |
||||
* Used to resume when the activity comes to the top of the stack |
||||
*/ |
||||
public void resumeJoysticks() { |
||||
if (sensorJoyInput != null) { |
||||
sensorJoyInput.resumeSensors(); |
||||
} |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void initialize() { |
||||
initialized = true; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isInitialized() { |
||||
return initialized; |
||||
} |
||||
|
||||
@Override |
||||
public void destroy() { |
||||
initialized = false; |
||||
|
||||
if (sensorJoyInput != null) { |
||||
sensorJoyInput.destroy(); |
||||
} |
||||
|
||||
setView(null); |
||||
} |
||||
|
||||
@Override |
||||
public void setInputListener(RawInputListener listener) { |
||||
this.listener = listener; |
||||
} |
||||
|
||||
@Override |
||||
public long getInputTimeNanos() { |
||||
return System.nanoTime(); |
||||
} |
||||
|
||||
@Override |
||||
public void setJoyRumble(int joyId, float amount) { |
||||
// convert amount to pulses since Android doesn't allow intensity
|
||||
if (vibrator != null) { |
||||
final long rumbleOnDur = (long)(amount * maxRumbleTime); // ms to pulse vibration on
|
||||
final long rumbleOffDur = maxRumbleTime - rumbleOnDur; // ms to delay between pulses
|
||||
final long[] rumblePattern = { |
||||
0, // start immediately
|
||||
rumbleOnDur, // time to leave vibration on
|
||||
rumbleOffDur // time to delay between vibrations
|
||||
}; |
||||
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});
|
||||
|
||||
if (rumbleOnDur > 0) { |
||||
vibrator.vibrate(rumblePattern, rumbleRepeatFrom); |
||||
vibratorActive = true; |
||||
} else { |
||||
vibrator.cancel(); |
||||
vibratorActive = false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Joystick[] loadJoysticks(InputManager inputManager) { |
||||
logger.log(Level.INFO, "loading joysticks for {0}", this.getClass().getName()); |
||||
if (!disableSensors) { |
||||
joystickList.add(sensorJoyInput.loadJoystick(joystickList.size(), inputManager)); |
||||
} |
||||
return joystickList.toArray( new Joystick[joystickList.size()] ); |
||||
} |
||||
|
||||
@Override |
||||
public void update() { |
||||
if (sensorJoyInput != null) { |
||||
sensorJoyInput.update(); |
||||
} |
||||
|
||||
if (listener != null) { |
||||
InputEvent inputEvent; |
||||
|
||||
while ((inputEvent = eventQueue.poll()) != null) { |
||||
if (inputEvent instanceof JoyAxisEvent) { |
||||
listener.onJoyAxisEvent((JoyAxisEvent)inputEvent); |
||||
} else if (inputEvent instanceof JoyButtonEvent) { |
||||
listener.onJoyButtonEvent((JoyButtonEvent)inputEvent); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,108 @@ |
||||
/* |
||||
* Copyright (c) 2009-2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.input.android; |
||||
|
||||
import android.view.KeyEvent; |
||||
import android.view.MotionEvent; |
||||
import com.jme3.input.InputManager; |
||||
import com.jme3.input.Joystick; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* <code>AndroidJoyInput14</code> extends <code>AndroidJoyInput</code> |
||||
* to include support for physical joysticks/gamepads.</br> |
||||
* |
||||
* @author iwgeric |
||||
*/ |
||||
public class AndroidJoyInput14 extends AndroidJoyInput { |
||||
private static final Logger logger = Logger.getLogger(AndroidJoyInput14.class.getName()); |
||||
|
||||
private AndroidJoystickJoyInput14 joystickJoyInput; |
||||
|
||||
public AndroidJoyInput14(AndroidInputHandler inputHandler) { |
||||
super(inputHandler); |
||||
joystickJoyInput = new AndroidJoystickJoyInput14(this); |
||||
} |
||||
|
||||
/** |
||||
* Pauses the joystick device listeners to save battery life if they are not needed. |
||||
* Used to pause when the activity pauses |
||||
*/ |
||||
@Override |
||||
public void pauseJoysticks() { |
||||
super.pauseJoysticks(); |
||||
|
||||
if (joystickJoyInput != null) { |
||||
joystickJoyInput.pauseJoysticks(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Resumes the joystick device listeners. |
||||
* Used to resume when the activity comes to the top of the stack |
||||
*/ |
||||
@Override |
||||
public void resumeJoysticks() { |
||||
super.resumeJoysticks(); |
||||
if (joystickJoyInput != null) { |
||||
joystickJoyInput.resumeJoysticks(); |
||||
} |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void destroy() { |
||||
super.destroy(); |
||||
if (joystickJoyInput != null) { |
||||
joystickJoyInput.destroy(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Joystick[] loadJoysticks(InputManager inputManager) { |
||||
// load the simulated joystick for device orientation
|
||||
super.loadJoysticks(inputManager); |
||||
// load physical gamepads/joysticks
|
||||
joystickList.addAll(joystickJoyInput.loadJoysticks(joystickList.size(), inputManager)); |
||||
// return the list of joysticks back to InputManager
|
||||
return joystickList.toArray( new Joystick[joystickList.size()] ); |
||||
} |
||||
|
||||
public boolean onGenericMotion(MotionEvent event) { |
||||
return joystickJoyInput.onGenericMotion(event); |
||||
} |
||||
|
||||
public boolean onKey(KeyEvent event) { |
||||
return joystickJoyInput.onKey(event); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,416 @@ |
||||
/* |
||||
* 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.JoystickCompatibilityMappings; |
||||
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()); |
||||
boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN; |
||||
if (button != null) { |
||||
JoyButtonEvent buttonEvent = new JoyButtonEvent(button, pressed); |
||||
joyInput.addEvent(buttonEvent); |
||||
consumed = true; |
||||
} else { |
||||
JoystickButton newButton = joystick.addButton(event.getKeyCode()); |
||||
JoyButtonEvent buttonEvent = new JoyButtonEvent(newButton, 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 original = KeyEvent.keyCodeToString(keyCode); |
||||
// A/B/X/Y buttons
|
||||
if (keyCode == KeyEvent.KEYCODE_BUTTON_Y) { |
||||
original = JoystickButton.BUTTON_0; |
||||
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_B) { |
||||
original = JoystickButton.BUTTON_1; |
||||
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_A) { |
||||
original = JoystickButton.BUTTON_2; |
||||
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_X) { |
||||
original = JoystickButton.BUTTON_3; |
||||
// Front buttons Some of these have the top ones and the bottoms ones flipped.
|
||||
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_L1) { |
||||
original = JoystickButton.BUTTON_4; |
||||
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_R1) { |
||||
original = JoystickButton.BUTTON_5; |
||||
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_L2) { |
||||
original = JoystickButton.BUTTON_6; |
||||
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_R2) { |
||||
original = JoystickButton.BUTTON_7; |
||||
// // Dpad buttons
|
||||
// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
|
||||
// original = JoystickButton.BUTTON_8;
|
||||
// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
|
||||
// original = JoystickButton.BUTTON_9;
|
||||
// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
|
||||
// original = JoystickButton.BUTTON_8;
|
||||
// } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
|
||||
// original = JoystickButton.BUTTON_9;
|
||||
// Select and start buttons
|
||||
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_SELECT) { |
||||
original = JoystickButton.BUTTON_8; |
||||
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_START) { |
||||
original = JoystickButton.BUTTON_9; |
||||
// Joystick push buttons
|
||||
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_THUMBL) { |
||||
original = JoystickButton.BUTTON_10; |
||||
} else if (keyCode == KeyEvent.KEYCODE_BUTTON_THUMBR) { |
||||
original = JoystickButton.BUTTON_11; |
||||
} |
||||
|
||||
String logicalId = JoystickCompatibilityMappings.remapComponent( getName(), original ); |
||||
if( logicalId == null ? original != null : !logicalId.equals(original) ) { |
||||
logger.log(Level.FINE, "Remapped: {0} to: {1}", |
||||
new Object[]{original, logicalId}); |
||||
} |
||||
|
||||
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 original = MotionEvent.axisToString(motionRange.getAxis()); |
||||
if (motionRange.getAxis() == MotionEvent.AXIS_X) { |
||||
original = JoystickAxis.X_AXIS; |
||||
} else if (motionRange.getAxis() == MotionEvent.AXIS_Y) { |
||||
original = JoystickAxis.Y_AXIS; |
||||
} else if (motionRange.getAxis() == MotionEvent.AXIS_Z) { |
||||
original = JoystickAxis.Z_AXIS; |
||||
} else if (motionRange.getAxis() == MotionEvent.AXIS_RZ) { |
||||
original = JoystickAxis.Z_ROTATION; |
||||
} else if (motionRange.getAxis() == MotionEvent.AXIS_HAT_X) { |
||||
original = JoystickAxis.POV_X; |
||||
} else if (motionRange.getAxis() == MotionEvent.AXIS_HAT_Y) { |
||||
original = JoystickAxis.POV_Y; |
||||
} |
||||
String logicalId = JoystickCompatibilityMappings.remapComponent( getName(), original ); |
||||
if( logicalId == null ? original != null : !logicalId.equals(original) ) { |
||||
logger.log(Level.FINE, "Remapped: {0} to: {1}", |
||||
new Object[]{original, logicalId}); |
||||
} |
||||
|
||||
JoystickAxis axis = new DefaultJoystickAxis(getInputManager(), |
||||
this, |
||||
getAxisCount(), |
||||
name, |
||||
logicalId, |
||||
true, |
||||
true, |
||||
motionRange.getFlat()); |
||||
|
||||
if (motionRange.getAxis() == MotionEvent.AXIS_X) { |
||||
xAxis = axis; |
||||
} |
||||
if (motionRange.getAxis() == MotionEvent.AXIS_Y) { |
||||
yAxis = axis; |
||||
} |
||||
if (motionRange.getAxis() == MotionEvent.AXIS_HAT_X) { |
||||
povX = axis; |
||||
} |
||||
if (motionRange.getAxis() == MotionEvent.AXIS_HAT_Y) { |
||||
povY = axis; |
||||
} |
||||
|
||||
addAxis(axis); |
||||
axisIndex.put(motionRange.getAxis(), axis); |
||||
return axis; |
||||
} |
||||
|
||||
@Override |
||||
public JoystickAxis getXAxis() { |
||||
return xAxis; |
||||
} |
||||
|
||||
@Override |
||||
public JoystickAxis getYAxis() { |
||||
return yAxis; |
||||
} |
||||
|
||||
@Override |
||||
public JoystickAxis getPovXAxis() { |
||||
return povX; |
||||
} |
||||
|
||||
@Override |
||||
public JoystickAxis getPovYAxis() { |
||||
return povY; |
||||
} |
||||
|
||||
@Override |
||||
public int getXAxisIndex(){ |
||||
return xAxis.getAxisId(); |
||||
} |
||||
|
||||
@Override |
||||
public int getYAxisIndex(){ |
||||
return yAxis.getAxisId(); |
||||
} |
||||
} |
||||
} |
@ -1,140 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.input.android; |
||||
|
||||
import android.content.Context; |
||||
import android.view.KeyEvent; |
||||
import android.view.View; |
||||
import android.view.inputmethod.InputMethodManager; |
||||
import com.jme3.input.event.KeyInputEvent; |
||||
import com.jme3.input.event.TouchEvent; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* AndroidKeyHandler recieves onKey events from the Android system and creates |
||||
* the jME KeyEvents. onKey is used by Android to receive keys from the keyboard |
||||
* or device buttons. All key events are consumed by jME except for the Volume |
||||
* buttons and menu button. |
||||
* |
||||
* This class also provides the functionality to display or hide the soft keyboard |
||||
* for inputing single key events. Use OGLESContext to display an dialog to type |
||||
* in complete strings. |
||||
* |
||||
* @author iwgeric |
||||
*/ |
||||
public class AndroidKeyHandler implements View.OnKeyListener { |
||||
private static final Logger logger = Logger.getLogger(AndroidKeyHandler.class.getName()); |
||||
|
||||
private AndroidInputHandler androidInput; |
||||
private boolean sendKeyEvents = true; |
||||
|
||||
public AndroidKeyHandler(AndroidInputHandler androidInput) { |
||||
this.androidInput = androidInput; |
||||
} |
||||
|
||||
public void initialize() { |
||||
} |
||||
|
||||
public void destroy() { |
||||
} |
||||
|
||||
public void setView(View view) { |
||||
if (view != null) { |
||||
view.setOnKeyListener(this); |
||||
} else { |
||||
androidInput.getView().setOnKeyListener(null); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* onKey gets called from android thread on key events |
||||
*/ |
||||
public boolean onKey(View view, int keyCode, KeyEvent event) { |
||||
if (androidInput.isInitialized() && view != androidInput.getView()) { |
||||
return false; |
||||
} |
||||
|
||||
TouchEvent evt; |
||||
// TODO: get touch event from pool
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) { |
||||
evt = new TouchEvent(); |
||||
evt.set(TouchEvent.Type.KEY_DOWN); |
||||
evt.setKeyCode(keyCode); |
||||
evt.setCharacters(event.getCharacters()); |
||||
evt.setTime(event.getEventTime()); |
||||
|
||||
// Send the event
|
||||
androidInput.addEvent(evt); |
||||
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) { |
||||
evt = new TouchEvent(); |
||||
evt.set(TouchEvent.Type.KEY_UP); |
||||
evt.setKeyCode(keyCode); |
||||
evt.setCharacters(event.getCharacters()); |
||||
evt.setTime(event.getEventTime()); |
||||
|
||||
// Send the event
|
||||
androidInput.addEvent(evt); |
||||
|
||||
} |
||||
|
||||
if (androidInput.isSimulateKeyboard()) { |
||||
KeyInputEvent kie; |
||||
char unicodeChar = (char)event.getUnicodeChar(); |
||||
int jmeKeyCode = AndroidKeyMapping.getJmeKey(keyCode); |
||||
|
||||
boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN; |
||||
boolean repeating = pressed && event.getRepeatCount() > 0; |
||||
|
||||
kie = new KeyInputEvent(jmeKeyCode, unicodeChar, pressed, repeating); |
||||
kie.setTime(event.getEventTime()); |
||||
androidInput.addEvent(kie); |
||||
// logger.log(Level.FINE, "onKey keyCode: {0}, jmeKeyCode: {1}, pressed: {2}, repeating: {3}",
|
||||
// new Object[]{keyCode, jmeKeyCode, pressed, repeating});
|
||||
// logger.log(Level.FINE, "creating KeyInputEvent: {0}", kie);
|
||||
} |
||||
|
||||
// consume all keys ourself except Volume Up/Down and Menu
|
||||
// Don't do Menu so that typical Android Menus can be created and used
|
||||
// by the user in MainActivity
|
||||
if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || |
||||
(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) || |
||||
(keyCode == KeyEvent.KEYCODE_MENU)) { |
||||
return false; |
||||
} else { |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -1,257 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.input.android; |
||||
|
||||
import android.view.MotionEvent; |
||||
import android.view.View; |
||||
import com.jme3.input.event.InputEvent; |
||||
import com.jme3.input.event.MouseButtonEvent; |
||||
import com.jme3.input.event.MouseMotionEvent; |
||||
import com.jme3.input.event.TouchEvent; |
||||
import static com.jme3.input.event.TouchEvent.Type.DOWN; |
||||
import static com.jme3.input.event.TouchEvent.Type.MOVE; |
||||
import static com.jme3.input.event.TouchEvent.Type.UP; |
||||
import com.jme3.math.Vector2f; |
||||
import java.util.HashMap; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* AndroidTouchHandler is the base class that receives touch inputs from the |
||||
* Android system and creates the TouchEvents for jME. This class is designed |
||||
* to handle the base touch events for Android rev 9 (Android 2.3). This is |
||||
* extended by other classes to add features that were introducted after |
||||
* Android rev 9. |
||||
* |
||||
* @author iwgeric |
||||
*/ |
||||
public class AndroidTouchHandler implements View.OnTouchListener { |
||||
private static final Logger logger = Logger.getLogger(AndroidTouchHandler.class.getName()); |
||||
|
||||
final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>(); |
||||
|
||||
protected int numPointers = 0; |
||||
|
||||
protected AndroidInputHandler androidInput; |
||||
protected AndroidGestureHandler gestureHandler; |
||||
|
||||
public AndroidTouchHandler(AndroidInputHandler androidInput, AndroidGestureHandler gestureHandler) { |
||||
this.androidInput = androidInput; |
||||
this.gestureHandler = gestureHandler; |
||||
} |
||||
|
||||
public void initialize() { |
||||
} |
||||
|
||||
public void destroy() { |
||||
setView(null); |
||||
} |
||||
|
||||
public void setView(View view) { |
||||
if (view != null) { |
||||
view.setOnTouchListener(this); |
||||
} else { |
||||
androidInput.getView().setOnTouchListener(null); |
||||
} |
||||
} |
||||
|
||||
protected int getPointerIndex(MotionEvent event) { |
||||
return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) |
||||
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT; |
||||
} |
||||
|
||||
protected int getPointerId(MotionEvent event) { |
||||
return event.getPointerId(getPointerIndex(event)); |
||||
} |
||||
|
||||
protected int getAction(MotionEvent event) { |
||||
return event.getAction() & MotionEvent.ACTION_MASK; |
||||
} |
||||
|
||||
/** |
||||
* onTouch gets called from android thread on touch events |
||||
*/ |
||||
public boolean onTouch(View view, MotionEvent event) { |
||||
if (!androidInput.isInitialized() || view != androidInput.getView()) { |
||||
return false; |
||||
} |
||||
|
||||
boolean bWasHandled = false; |
||||
TouchEvent touch = null; |
||||
// System.out.println("native : " + event.getAction());
|
||||
int action = getAction(event); |
||||
int pointerIndex = getPointerIndex(event); |
||||
int pointerId = getPointerId(event); |
||||
Vector2f lastPos = lastPositions.get(pointerId); |
||||
float jmeX; |
||||
float jmeY; |
||||
|
||||
numPointers = event.getPointerCount(); |
||||
|
||||
// final int historySize = event.getHistorySize();
|
||||
//final int pointerCount = event.getPointerCount();
|
||||
switch (getAction(event)) { |
||||
case MotionEvent.ACTION_POINTER_DOWN: |
||||
case MotionEvent.ACTION_DOWN: |
||||
jmeX = androidInput.getJmeX(event.getX(pointerIndex)); |
||||
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex))); |
||||
touch = androidInput.getFreeTouchEvent(); |
||||
touch.set(TouchEvent.Type.DOWN, jmeX, jmeY, 0, 0); |
||||
touch.setPointerId(pointerId); |
||||
touch.setTime(event.getEventTime()); |
||||
touch.setPressure(event.getPressure(pointerIndex)); |
||||
|
||||
lastPos = new Vector2f(jmeX, jmeY); |
||||
lastPositions.put(pointerId, lastPos); |
||||
|
||||
processEvent(touch); |
||||
|
||||
bWasHandled = true; |
||||
break; |
||||
case MotionEvent.ACTION_POINTER_UP: |
||||
case MotionEvent.ACTION_CANCEL: |
||||
case MotionEvent.ACTION_UP: |
||||
jmeX = androidInput.getJmeX(event.getX(pointerIndex)); |
||||
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(pointerIndex))); |
||||
touch = androidInput.getFreeTouchEvent(); |
||||
touch.set(TouchEvent.Type.UP, jmeX, jmeY, 0, 0); |
||||
touch.setPointerId(pointerId); |
||||
touch.setTime(event.getEventTime()); |
||||
touch.setPressure(event.getPressure(pointerIndex)); |
||||
lastPositions.remove(pointerId); |
||||
|
||||
processEvent(touch); |
||||
|
||||
bWasHandled = true; |
||||
break; |
||||
case MotionEvent.ACTION_MOVE: |
||||
// Convert all pointers into events
|
||||
for (int p = 0; p < event.getPointerCount(); p++) { |
||||
jmeX = androidInput.getJmeX(event.getX(p)); |
||||
jmeY = androidInput.invertY(androidInput.getJmeY(event.getY(p))); |
||||
lastPos = lastPositions.get(event.getPointerId(p)); |
||||
if (lastPos == null) { |
||||
lastPos = new Vector2f(jmeX, jmeY); |
||||
lastPositions.put(event.getPointerId(p), lastPos); |
||||
} |
||||
|
||||
float dX = jmeX - lastPos.x; |
||||
float dY = jmeY - lastPos.y; |
||||
if (dX != 0 || dY != 0) { |
||||
touch = androidInput.getFreeTouchEvent(); |
||||
touch.set(TouchEvent.Type.MOVE, jmeX, jmeY, dX, dY); |
||||
touch.setPointerId(event.getPointerId(p)); |
||||
touch.setTime(event.getEventTime()); |
||||
touch.setPressure(event.getPressure(p)); |
||||
lastPos.set(jmeX, jmeY); |
||||
|
||||
processEvent(touch); |
||||
|
||||
bWasHandled = true; |
||||
} |
||||
} |
||||
break; |
||||
case MotionEvent.ACTION_OUTSIDE: |
||||
break; |
||||
|
||||
} |
||||
|
||||
// Try to detect gestures
|
||||
if (gestureHandler != null) { |
||||
gestureHandler.detectGesture(event); |
||||
} |
||||
|
||||
return bWasHandled; |
||||
} |
||||
|
||||
protected void processEvent(TouchEvent event) { |
||||
// Add the touch event
|
||||
androidInput.addEvent(event); |
||||
// MouseEvents do not support multi-touch, so only evaluate 1 finger pointer events
|
||||
if (androidInput.isSimulateMouse() && numPointers == 1) { |
||||
InputEvent mouseEvent = generateMouseEvent(event); |
||||
if (mouseEvent != null) { |
||||
// Add the mouse event
|
||||
androidInput.addEvent(mouseEvent); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
// TODO: Ring Buffer for mouse events?
|
||||
protected InputEvent generateMouseEvent(TouchEvent event) { |
||||
InputEvent inputEvent = null; |
||||
int newX; |
||||
int newY; |
||||
int newDX; |
||||
int newDY; |
||||
|
||||
if (androidInput.isMouseEventsInvertX()) { |
||||
newX = (int) (androidInput.invertX(event.getX())); |
||||
newDX = (int)event.getDeltaX() * -1; |
||||
} else { |
||||
newX = (int) event.getX(); |
||||
newDX = (int)event.getDeltaX(); |
||||
} |
||||
|
||||
if (androidInput.isMouseEventsInvertY()) { |
||||
newY = (int) (androidInput.invertY(event.getY())); |
||||
newDY = (int)event.getDeltaY() * -1; |
||||
} else { |
||||
newY = (int) event.getY(); |
||||
newDY = (int)event.getDeltaY(); |
||||
} |
||||
|
||||
switch (event.getType()) { |
||||
case DOWN: |
||||
// Handle mouse down event
|
||||
inputEvent = new MouseButtonEvent(0, true, newX, newY); |
||||
inputEvent.setTime(event.getTime()); |
||||
break; |
||||
|
||||
case UP: |
||||
// Handle mouse up event
|
||||
inputEvent = new MouseButtonEvent(0, false, newX, newY); |
||||
inputEvent.setTime(event.getTime()); |
||||
break; |
||||
|
||||
case HOVER_MOVE: |
||||
case MOVE: |
||||
inputEvent = new MouseMotionEvent(newX, newY, newDX, newDY, (int)event.getScaleSpan(), (int)event.getDeltaScaleSpan()); |
||||
inputEvent.setTime(event.getTime()); |
||||
break; |
||||
} |
||||
|
||||
return inputEvent; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,475 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.input.android; |
||||
|
||||
import android.view.GestureDetector; |
||||
import android.view.KeyEvent; |
||||
import android.view.MotionEvent; |
||||
import android.view.ScaleGestureDetector; |
||||
import com.jme3.input.RawInputListener; |
||||
import com.jme3.input.TouchInput; |
||||
import com.jme3.input.event.InputEvent; |
||||
import com.jme3.input.event.KeyInputEvent; |
||||
import com.jme3.input.event.MouseButtonEvent; |
||||
import com.jme3.input.event.MouseMotionEvent; |
||||
import com.jme3.input.event.TouchEvent; |
||||
import static com.jme3.input.event.TouchEvent.Type.DOWN; |
||||
import static com.jme3.input.event.TouchEvent.Type.MOVE; |
||||
import static com.jme3.input.event.TouchEvent.Type.UP; |
||||
import com.jme3.math.Vector2f; |
||||
import com.jme3.system.AppSettings; |
||||
import java.util.HashMap; |
||||
import java.util.concurrent.ConcurrentLinkedQueue; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* AndroidTouchInput is the base class that receives touch inputs from the |
||||
* Android system and creates the TouchEvents for jME. This class is designed |
||||
* to handle the base touch events for Android rev 9 (Android 2.3). This is |
||||
* extended by other classes to add features that were introducted after |
||||
* Android rev 9. |
||||
* |
||||
* @author iwgeric |
||||
*/ |
||||
public class AndroidTouchInput implements TouchInput { |
||||
private static final Logger logger = Logger.getLogger(AndroidTouchInput.class.getName()); |
||||
|
||||
private boolean mouseEventsEnabled = true; |
||||
private boolean mouseEventsInvertX = false; |
||||
private boolean mouseEventsInvertY = false; |
||||
private boolean keyboardEventsEnabled = false; |
||||
private boolean dontSendHistory = false; |
||||
|
||||
protected int numPointers = 0; |
||||
final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>(); |
||||
final private ConcurrentLinkedQueue<InputEvent> inputEventQueue = new ConcurrentLinkedQueue<InputEvent>(); |
||||
private final static int MAX_TOUCH_EVENTS = 1024; |
||||
private final TouchEventPool touchEventPool = new TouchEventPool(MAX_TOUCH_EVENTS); |
||||
private float scaleX = 1f; |
||||
private float scaleY = 1f; |
||||
|
||||
private boolean initialized = false; |
||||
private RawInputListener listener = null; |
||||
|
||||
private GestureDetector gestureDetector; |
||||
private ScaleGestureDetector scaleDetector; |
||||
|
||||
protected AndroidInputHandler androidInput; |
||||
|
||||
public AndroidTouchInput(AndroidInputHandler androidInput) { |
||||
this.androidInput = androidInput; |
||||
} |
||||
|
||||
public GestureDetector getGestureDetector() { |
||||
return gestureDetector; |
||||
} |
||||
|
||||
public void setGestureDetector(GestureDetector gestureDetector) { |
||||
this.gestureDetector = gestureDetector; |
||||
} |
||||
|
||||
public ScaleGestureDetector getScaleDetector() { |
||||
return scaleDetector; |
||||
} |
||||
|
||||
public void setScaleDetector(ScaleGestureDetector scaleDetector) { |
||||
this.scaleDetector = scaleDetector; |
||||
} |
||||
|
||||
public float invertX(float origX) { |
||||
return getJmeX(androidInput.getView().getWidth()) - origX; |
||||
} |
||||
|
||||
public float invertY(float origY) { |
||||
return getJmeY(androidInput.getView().getHeight()) - origY; |
||||
} |
||||
|
||||
public float getJmeX(float origX) { |
||||
return origX * scaleX; |
||||
} |
||||
|
||||
public float getJmeY(float origY) { |
||||
return origY * scaleY; |
||||
} |
||||
|
||||
public void loadSettings(AppSettings settings) { |
||||
keyboardEventsEnabled = settings.isEmulateKeyboard(); |
||||
mouseEventsEnabled = settings.isEmulateMouse(); |
||||
mouseEventsInvertX = settings.isEmulateMouseFlipX(); |
||||
mouseEventsInvertY = settings.isEmulateMouseFlipY(); |
||||
|
||||
// view width and height are 0 until the view is displayed on the screen
|
||||
if (androidInput.getView().getWidth() != 0 && androidInput.getView().getHeight() != 0) { |
||||
scaleX = (float)settings.getWidth() / (float)androidInput.getView().getWidth(); |
||||
scaleY = (float)settings.getHeight() / (float)androidInput.getView().getHeight(); |
||||
} |
||||
logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}", |
||||
new Object[]{scaleX, scaleY}); |
||||
|
||||
|
||||
} |
||||
|
||||
|
||||
protected int getPointerIndex(MotionEvent event) { |
||||
return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) |
||||
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT; |
||||
} |
||||
|
||||
protected int getPointerId(MotionEvent event) { |
||||
return event.getPointerId(getPointerIndex(event)); |
||||
} |
||||
|
||||
protected int getAction(MotionEvent event) { |
||||
return event.getAction() & MotionEvent.ACTION_MASK; |
||||
} |
||||
|
||||
public boolean onTouch(MotionEvent event) { |
||||
if (!isInitialized()) { |
||||
return false; |
||||
} |
||||
|
||||
boolean bWasHandled = false; |
||||
TouchEvent touch = null; |
||||
// System.out.println("native : " + event.getAction());
|
||||
int action = getAction(event); |
||||
int pointerIndex = getPointerIndex(event); |
||||
int pointerId = getPointerId(event); |
||||
Vector2f lastPos = lastPositions.get(pointerId); |
||||
float jmeX; |
||||
float jmeY; |
||||
|
||||
numPointers = event.getPointerCount(); |
||||
|
||||
// final int historySize = event.getHistorySize();
|
||||
//final int pointerCount = event.getPointerCount();
|
||||
switch (getAction(event)) { |
||||
case MotionEvent.ACTION_POINTER_DOWN: |
||||
case MotionEvent.ACTION_DOWN: |
||||
jmeX = getJmeX(event.getX(pointerIndex)); |
||||
jmeY = invertY(getJmeY(event.getY(pointerIndex))); |
||||
touch = getFreeTouchEvent(); |
||||
touch.set(TouchEvent.Type.DOWN, jmeX, jmeY, 0, 0); |
||||
touch.setPointerId(pointerId); |
||||
touch.setTime(event.getEventTime()); |
||||
touch.setPressure(event.getPressure(pointerIndex)); |
||||
|
||||
lastPos = new Vector2f(jmeX, jmeY); |
||||
lastPositions.put(pointerId, lastPos); |
||||
|
||||
addEvent(touch); |
||||
addEvent(generateMouseEvent(touch)); |
||||
|
||||
bWasHandled = true; |
||||
break; |
||||
case MotionEvent.ACTION_POINTER_UP: |
||||
case MotionEvent.ACTION_CANCEL: |
||||
case MotionEvent.ACTION_UP: |
||||
jmeX = getJmeX(event.getX(pointerIndex)); |
||||
jmeY = invertY(getJmeY(event.getY(pointerIndex))); |
||||
touch = getFreeTouchEvent(); |
||||
touch.set(TouchEvent.Type.UP, jmeX, jmeY, 0, 0); |
||||
touch.setPointerId(pointerId); |
||||
touch.setTime(event.getEventTime()); |
||||
touch.setPressure(event.getPressure(pointerIndex)); |
||||
lastPositions.remove(pointerId); |
||||
|
||||
addEvent(touch); |
||||
addEvent(generateMouseEvent(touch)); |
||||
|
||||
bWasHandled = true; |
||||
break; |
||||
case MotionEvent.ACTION_MOVE: |
||||
// Convert all pointers into events
|
||||
for (int p = 0; p < event.getPointerCount(); p++) { |
||||
jmeX = getJmeX(event.getX(p)); |
||||
jmeY = invertY(getJmeY(event.getY(p))); |
||||
lastPos = lastPositions.get(event.getPointerId(p)); |
||||
if (lastPos == null) { |
||||
lastPos = new Vector2f(jmeX, jmeY); |
||||
lastPositions.put(event.getPointerId(p), lastPos); |
||||
} |
||||
|
||||
float dX = jmeX - lastPos.x; |
||||
float dY = jmeY - lastPos.y; |
||||
if (dX != 0 || dY != 0) { |
||||
touch = getFreeTouchEvent(); |
||||
touch.set(TouchEvent.Type.MOVE, jmeX, jmeY, dX, dY); |
||||
touch.setPointerId(event.getPointerId(p)); |
||||
touch.setTime(event.getEventTime()); |
||||
touch.setPressure(event.getPressure(p)); |
||||
lastPos.set(jmeX, jmeY); |
||||
|
||||
addEvent(touch); |
||||
addEvent(generateMouseEvent(touch)); |
||||
|
||||
bWasHandled = true; |
||||
} |
||||
} |
||||
break; |
||||
case MotionEvent.ACTION_OUTSIDE: |
||||
break; |
||||
|
||||
} |
||||
|
||||
// Try to detect gestures
|
||||
if (gestureDetector != null) { |
||||
gestureDetector.onTouchEvent(event); |
||||
} |
||||
if (scaleDetector != null) { |
||||
scaleDetector.onTouchEvent(event); |
||||
} |
||||
|
||||
return bWasHandled; |
||||
} |
||||
|
||||
// TODO: Ring Buffer for mouse events?
|
||||
public InputEvent generateMouseEvent(TouchEvent event) { |
||||
InputEvent inputEvent = null; |
||||
int newX; |
||||
int newY; |
||||
int newDX; |
||||
int newDY; |
||||
|
||||
// MouseEvents do not support multi-touch, so only evaluate 1 finger pointer events
|
||||
if (!isSimulateMouse() || numPointers > 1) { |
||||
return null; |
||||
} |
||||
|
||||
|
||||
if (isMouseEventsInvertX()) { |
||||
newX = (int) (invertX(event.getX())); |
||||
newDX = (int)event.getDeltaX() * -1; |
||||
} else { |
||||
newX = (int) event.getX(); |
||||
newDX = (int)event.getDeltaX(); |
||||
} |
||||
|
||||
if (isMouseEventsInvertY()) { |
||||
newY = (int) (invertY(event.getY())); |
||||
newDY = (int)event.getDeltaY() * -1; |
||||
} else { |
||||
newY = (int) event.getY(); |
||||
newDY = (int)event.getDeltaY(); |
||||
} |
||||
|
||||
switch (event.getType()) { |
||||
case DOWN: |
||||
// Handle mouse down event
|
||||
inputEvent = new MouseButtonEvent(0, true, newX, newY); |
||||
inputEvent.setTime(event.getTime()); |
||||
break; |
||||
|
||||
case UP: |
||||
// Handle mouse up event
|
||||
inputEvent = new MouseButtonEvent(0, false, newX, newY); |
||||
inputEvent.setTime(event.getTime()); |
||||
break; |
||||
|
||||
case HOVER_MOVE: |
||||
case MOVE: |
||||
inputEvent = new MouseMotionEvent(newX, newY, newDX, newDY, (int)event.getScaleSpan(), (int)event.getDeltaScaleSpan()); |
||||
inputEvent.setTime(event.getTime()); |
||||
break; |
||||
} |
||||
|
||||
return inputEvent; |
||||
} |
||||
|
||||
|
||||
public boolean onKey(KeyEvent event) { |
||||
if (!isInitialized()) { |
||||
return false; |
||||
} |
||||
|
||||
TouchEvent evt; |
||||
// TODO: get touch event from pool
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) { |
||||
evt = new TouchEvent(); |
||||
evt.set(TouchEvent.Type.KEY_DOWN); |
||||
evt.setKeyCode(event.getKeyCode()); |
||||
evt.setCharacters(event.getCharacters()); |
||||
evt.setTime(event.getEventTime()); |
||||
|
||||
// Send the event
|
||||
addEvent(evt); |
||||
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) { |
||||
evt = new TouchEvent(); |
||||
evt.set(TouchEvent.Type.KEY_UP); |
||||
evt.setKeyCode(event.getKeyCode()); |
||||
evt.setCharacters(event.getCharacters()); |
||||
evt.setTime(event.getEventTime()); |
||||
|
||||
// Send the event
|
||||
addEvent(evt); |
||||
|
||||
} |
||||
|
||||
if (isSimulateKeyboard()) { |
||||
KeyInputEvent kie; |
||||
char unicodeChar = (char)event.getUnicodeChar(); |
||||
int jmeKeyCode = AndroidKeyMapping.getJmeKey(event.getKeyCode()); |
||||
|
||||
boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN; |
||||
boolean repeating = pressed && event.getRepeatCount() > 0; |
||||
|
||||
kie = new KeyInputEvent(jmeKeyCode, unicodeChar, pressed, repeating); |
||||
kie.setTime(event.getEventTime()); |
||||
addEvent(kie); |
||||
// logger.log(Level.FINE, "onKey keyCode: {0}, jmeKeyCode: {1}, pressed: {2}, repeating: {3}",
|
||||
// new Object[]{event.getKeyCode(), jmeKeyCode, pressed, repeating});
|
||||
// logger.log(Level.FINE, "creating KeyInputEvent: {0}", kie);
|
||||
} |
||||
|
||||
// consume all keys ourself except Volume Up/Down and Menu
|
||||
// Don't do Menu so that typical Android Menus can be created and used
|
||||
// by the user in MainActivity
|
||||
if ((event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) || |
||||
(event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) || |
||||
(event.getKeyCode() == KeyEvent.KEYCODE_MENU)) { |
||||
return false; |
||||
} else { |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------
|
||||
// JME3 Input interface
|
||||
@Override |
||||
public void initialize() { |
||||
touchEventPool.initialize(); |
||||
|
||||
initialized = true; |
||||
} |
||||
|
||||
@Override |
||||
public void destroy() { |
||||
initialized = false; |
||||
|
||||
touchEventPool.destroy(); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public boolean isInitialized() { |
||||
return initialized; |
||||
} |
||||
|
||||
@Override |
||||
public void setInputListener(RawInputListener listener) { |
||||
this.listener = listener; |
||||
} |
||||
|
||||
@Override |
||||
public long getInputTimeNanos() { |
||||
return System.nanoTime(); |
||||
} |
||||
|
||||
@Override |
||||
public void update() { |
||||
if (listener != null) { |
||||
InputEvent inputEvent; |
||||
|
||||
while ((inputEvent = inputEventQueue.poll()) != null) { |
||||
if (inputEvent instanceof TouchEvent) { |
||||
listener.onTouchEvent((TouchEvent)inputEvent); |
||||
} else if (inputEvent instanceof MouseButtonEvent) { |
||||
listener.onMouseButtonEvent((MouseButtonEvent)inputEvent); |
||||
} else if (inputEvent instanceof MouseMotionEvent) { |
||||
listener.onMouseMotionEvent((MouseMotionEvent)inputEvent); |
||||
} else if (inputEvent instanceof KeyInputEvent) { |
||||
listener.onKeyEvent((KeyInputEvent)inputEvent); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// -----------------------------------------
|
||||
|
||||
public TouchEvent getFreeTouchEvent() { |
||||
return touchEventPool.getNextFreeEvent(); |
||||
} |
||||
|
||||
public void addEvent(InputEvent event) { |
||||
if (event == null) { |
||||
return; |
||||
} |
||||
|
||||
logger.log(Level.INFO, "event: {0}", event); |
||||
|
||||
inputEventQueue.add(event); |
||||
if (event instanceof TouchEvent) { |
||||
touchEventPool.storeEvent((TouchEvent)event); |
||||
} |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void setSimulateMouse(boolean simulate) { |
||||
this.mouseEventsEnabled = simulate; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSimulateMouse() { |
||||
return mouseEventsEnabled; |
||||
} |
||||
|
||||
public boolean isMouseEventsInvertX() { |
||||
return mouseEventsInvertX; |
||||
} |
||||
|
||||
public boolean isMouseEventsInvertY() { |
||||
return mouseEventsInvertY; |
||||
} |
||||
|
||||
@Override |
||||
public void setSimulateKeyboard(boolean simulate) { |
||||
this.keyboardEventsEnabled = simulate; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSimulateKeyboard() { |
||||
return keyboardEventsEnabled; |
||||
} |
||||
|
||||
@Override |
||||
public void setOmitHistoricEvents(boolean dontSendHistory) { |
||||
this.dontSendHistory = dontSendHistory; |
||||
} |
||||
|
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,214 @@ |
||||
package com.jme3.scene.plugins.blender.math; |
||||
|
||||
import java.text.DecimalFormat; |
||||
|
||||
import org.ejml.ops.CommonOps; |
||||
import org.ejml.simple.SimpleMatrix; |
||||
import org.ejml.simple.SimpleSVD; |
||||
|
||||
import com.jme3.math.FastMath; |
||||
|
||||
/** |
||||
* Encapsulates a 4x4 matrix |
||||
* |
||||
* |
||||
*/ |
||||
public class Matrix extends SimpleMatrix { |
||||
private static final long serialVersionUID = 2396600537315902559L; |
||||
|
||||
public Matrix(int rows, int cols) { |
||||
super(rows, cols); |
||||
} |
||||
|
||||
/** |
||||
* Copy constructor |
||||
*/ |
||||
public Matrix(SimpleMatrix m) { |
||||
super(m); |
||||
} |
||||
|
||||
public Matrix(double[][] data) { |
||||
super(data); |
||||
} |
||||
|
||||
public static Matrix identity(int size) { |
||||
Matrix result = new Matrix(size, size); |
||||
CommonOps.setIdentity(result.mat); |
||||
return result; |
||||
} |
||||
|
||||
public Matrix pseudoinverse() { |
||||
return this.pseudoinverse(1); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public Matrix pseudoinverse(double lambda) { |
||||
SimpleSVD<SimpleMatrix> simpleSVD = this.svd(); |
||||
|
||||
SimpleMatrix U = simpleSVD.getU(); |
||||
SimpleMatrix S = simpleSVD.getW(); |
||||
SimpleMatrix V = simpleSVD.getV(); |
||||
|
||||
int N = Math.min(this.numRows(),this.numCols()); |
||||
double maxSingular = 0; |
||||
for( int i = 0; i < N; ++i ) { |
||||
if( S.get(i, i) > maxSingular ) { |
||||
maxSingular = S.get(i, i); |
||||
} |
||||
} |
||||
|
||||
double tolerance = FastMath.DBL_EPSILON * Math.max(this.numRows(),this.numCols()) * maxSingular; |
||||
for(int i=0;i<Math.min(S.numRows(), S.numCols());++i) { |
||||
double a = S.get(i, i); |
||||
if(a <= tolerance) { |
||||
a = 0; |
||||
} else { |
||||
a = a/(a * a + lambda * lambda); |
||||
} |
||||
S.set(i, i, a); |
||||
} |
||||
return new Matrix(V.mult(S.transpose()).mult(U.transpose())); |
||||
} |
||||
|
||||
public void setColumn(Vector3d col, int column) { |
||||
this.setColumn(column, 0, col.x, col.y, col.z); |
||||
} |
||||
|
||||
/** |
||||
* Just for some debug informations in order to compare the results with the scilab computation program. |
||||
* @param name the name of the matrix |
||||
* @param m the matrix to print out |
||||
* @return the String format of the matrix to easily input it to Scilab |
||||
*/ |
||||
public String toScilabString(String name, SimpleMatrix m) { |
||||
String result = name + " = ["; |
||||
|
||||
for(int i=0;i<m.numRows();++i) { |
||||
for(int j=0;j<m.numCols();++j) { |
||||
result += m.get(i, j) + " "; |
||||
} |
||||
result += ";"; |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* @return a String representation of the matrix |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
DecimalFormat df = new DecimalFormat("#.0000"); |
||||
StringBuilder buf = new StringBuilder(); |
||||
for (int r = 0; r < this.numRows(); ++r) { |
||||
buf.append("\n| "); |
||||
for (int c = 0; c < this.numCols(); ++c) { |
||||
buf.append(df.format(this.get(r, c))).append(' '); |
||||
} |
||||
buf.append('|'); |
||||
} |
||||
return buf.toString(); |
||||
} |
||||
|
||||
public void setTranslation(Vector3d translation) { |
||||
this.setColumn(translation, 3); |
||||
} |
||||
|
||||
/** |
||||
* Sets the scale. |
||||
* |
||||
* @param scale |
||||
* the scale vector to set |
||||
*/ |
||||
public void setScale(Vector3d scale) { |
||||
this.setScale(scale.x, scale.y, scale.z); |
||||
} |
||||
|
||||
/** |
||||
* Sets the scale. |
||||
* |
||||
* @param x |
||||
* the X scale |
||||
* @param y |
||||
* the Y scale |
||||
* @param z |
||||
* the Z scale |
||||
*/ |
||||
public void setScale(double x, double y, double z) { |
||||
Vector3d vect1 = new Vector3d(this.get(0, 0), this.get(1, 0), this.get(2, 0)); |
||||
vect1.normalizeLocal().multLocal(x); |
||||
this.set(0, 0, vect1.x); |
||||
this.set(1, 0, vect1.y); |
||||
this.set(2, 0, vect1.z); |
||||
|
||||
vect1.set(this.get(0, 1), this.get(1, 1), this.get(2, 1)); |
||||
vect1.normalizeLocal().multLocal(y); |
||||
this.set(0, 1, vect1.x); |
||||
this.set(1, 1, vect1.y); |
||||
this.set(2, 1, vect1.z); |
||||
|
||||
vect1.set(this.get(0, 2), this.get(1, 2), this.get(2, 2)); |
||||
vect1.normalizeLocal().multLocal(z); |
||||
this.set(0, 2, vect1.x); |
||||
this.set(1, 2, vect1.y); |
||||
this.set(2, 2, vect1.z); |
||||
} |
||||
|
||||
/** |
||||
* <code>setRotationQuaternion</code> builds a rotation from a |
||||
* <code>Quaternion</code>. |
||||
* |
||||
* @param quat |
||||
* the quaternion to build the rotation from. |
||||
* @throws NullPointerException |
||||
* if quat is null. |
||||
*/ |
||||
public void setRotationQuaternion(DQuaternion quat) { |
||||
quat.toRotationMatrix(this); |
||||
} |
||||
|
||||
public DTransform toTransform() { |
||||
DTransform result = new DTransform(); |
||||
result.setTranslation(this.toTranslationVector()); |
||||
result.setRotation(this.toRotationQuat()); |
||||
result.setScale(this.toScaleVector()); |
||||
return result; |
||||
} |
||||
|
||||
public Vector3d toTranslationVector() { |
||||
return new Vector3d(this.get(0, 3), this.get(1, 3), this.get(2, 3)); |
||||
} |
||||
|
||||
public DQuaternion toRotationQuat() { |
||||
DQuaternion quat = new DQuaternion(); |
||||
quat.fromRotationMatrix(this.get(0, 0), this.get(0, 1), this.get(0, 2), this.get(1, 0), this.get(1, 1), this.get(1, 2), this.get(2, 0), this.get(2, 1), this.get(2, 2)); |
||||
return quat; |
||||
} |
||||
|
||||
/** |
||||
* Retreives the scale vector from the matrix and stores it into a given |
||||
* vector. |
||||
* |
||||
* @param the |
||||
* vector where the scale will be stored |
||||
*/ |
||||
public Vector3d toScaleVector() { |
||||
Vector3d result = new Vector3d(); |
||||
this.toScaleVector(result); |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Retreives the scale vector from the matrix and stores it into a given |
||||
* vector. |
||||
* |
||||
* @param the |
||||
* vector where the scale will be stored |
||||
*/ |
||||
public void toScaleVector(Vector3d vector) { |
||||
double scaleX = Math.sqrt(this.get(0, 0) * this.get(0, 0) + this.get(1, 0) * this.get(1, 0) + this.get(2, 0) * this.get(2, 0)); |
||||
double scaleY = Math.sqrt(this.get(0, 1) * this.get(0, 1) + this.get(1, 1) * this.get(1, 1) + this.get(2, 1) * this.get(2, 1)); |
||||
double scaleZ = Math.sqrt(this.get(0, 2) * this.get(0, 2) + this.get(1, 2) * this.get(1, 2) + this.get(2, 2) * this.get(2, 2)); |
||||
vector.set(scaleX, scaleY, scaleZ); |
||||
} |
||||
} |
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue