diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInputHandler.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInputHandler.java new file mode 100644 index 000000000..2c3a1d74e --- /dev/null +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInputHandler.java @@ -0,0 +1,257 @@ +/* + * 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.Build; +import android.os.Vibrator; +import android.view.View; +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.
+ * 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 + * + * + * @author iwgeric + */ +public class AndroidJoyInputHandler implements JoyInput { + private static final Logger logger = Logger.getLogger(AndroidJoyInputHandler.class.getName()); + + private List joystickList = new ArrayList(); +// private boolean dontSendHistory = false; + + + // Internal + private GLSurfaceView view; + private boolean initialized = false; + private RawInputListener listener = null; + private ConcurrentLinkedQueue eventQueue = new ConcurrentLinkedQueue(); + private AndroidSensorJoyInput sensorJoyInput; + private Vibrator vibrator = null; + private boolean vibratorActive = false; + private long maxRumbleTime = 250; // 250ms + + public AndroidJoyInputHandler() { + int buildVersion = Build.VERSION.SDK_INT; + logger.log(Level.INFO, "Android Build Version: {0}", buildVersion); +// if (buildVersion >= 14) { +// touchHandler = new AndroidTouchHandler14(this); +// } else if (buildVersion >= 8){ +// touchHandler = new AndroidTouchHandler(this); +// } + sensorJoyInput = new AndroidSensorJoyInput(this); + } + + public void setView(GLSurfaceView view) { +// if (touchHandler != null) { +// touchHandler.setView(view); +// } + if (sensorJoyInput != null) { + sensorJoyInput.setView(view); + } + this.view = (GLSurfaceView)view; + + } + + public View getView() { + return view; + } + + public void loadSettings(AppSettings settings) { +// sensorEventsEnabled = settings.useSensors(); + } + + public void addEvent(InputEvent event) { + 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() { +// if (sensorJoyInput != null) { +// sensorJoyInput.initialize(); +// } + // Get instance of Vibrator from current Context + vibrator = (Vibrator) view.getContext().getSystemService(Context.VIBRATOR_SERVICE); + if (vibrator == null) { + logger.log(Level.FINE, "Vibrator Service not found."); + } + initialized = true; + } + + @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) { + 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); + } + } + } + + } + + + +} diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java index 034b068d8..823aa3d9d 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java @@ -37,7 +37,7 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; -import android.os.Vibrator; +import android.opengl.GLSurfaceView; import android.view.Surface; import android.view.WindowManager; import com.jme3.input.AbstractJoystick; @@ -47,10 +47,8 @@ import com.jme3.input.JoyInput; import com.jme3.input.Joystick; import com.jme3.input.JoystickAxis; import com.jme3.input.SensorJoystickAxis; -import com.jme3.input.RawInputListener; import com.jme3.input.event.JoyAxisEvent; import com.jme3.math.FastMath; -import com.jme3.system.android.JmeAndroidSystem; import com.jme3.util.IntMap; import com.jme3.util.IntMap.Entry; import java.util.ArrayList; @@ -63,7 +61,7 @@ import java.util.logging.Logger; * A single joystick is configured and includes data for all configured sensors * as seperate axes of the joystick. * - * Each axis is named accounting to the static strings in SensorJoystickAxis. + * Each axis is named according to the static strings in SensorJoystickAxis. * Refer to the strings defined in SensorJoystickAxis for a list of supported * sensors and their axis data. Each sensor type defined in SensorJoystickAxis * will be attempted to be configured. If the device does not support a particular @@ -72,46 +70,21 @@ import java.util.logging.Logger; * The joystick.getXAxis and getYAxis methods of the joystick are configured to * return the device orientation values in the device's X and Y directions. * - * This joystick 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 is not required - * by the application. - * - * To use the joystick rumble feature, the following line needs to be - * added to the Android Manifest File - * - * * @author iwgeric */ -public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { +public class AndroidSensorJoyInput implements SensorEventListener { private final static Logger logger = Logger.getLogger(AndroidSensorJoyInput.class.getName()); - private Context context = null; - private InputManager inputManager = null; + private AndroidJoyInputHandler joyHandler; private SensorManager sensorManager = null; private WindowManager windowManager = null; - private Vibrator vibrator = null; - private boolean vibratorActive = false; - private long maxRumbleTime = 250; // 250ms - private RawInputListener listener = null; private IntMap sensors = new IntMap(); - private AndroidJoystick[] joysticks; private int lastRotation = 0; - private boolean initialized = false; private boolean loaded = false; - private final ArrayList eventQueue = new ArrayList(); + public AndroidSensorJoyInput(AndroidJoyInputHandler joyHandler) { + this.joyHandler = joyHandler; + } /** * Internal class to enclose data for each sensor. @@ -120,7 +93,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { int androidSensorType = -1; int androidSensorSpeed = SensorManager.SENSOR_DELAY_GAME; Sensor sensor = null; - int sensorAccuracy = 0; + int sensorAccuracy = -1; float[] lastValues; final Object valuesLock = new Object(); ArrayList axes = new ArrayList(); @@ -134,16 +107,19 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { } - private void initSensorManager() { - this.context = JmeAndroidSystem.getView().getContext(); - // Get instance of the WindowManager from the current Context - windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - // Get instance of the SensorManager from the current Context - sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - // Get instance of Vibrator from current Context - vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); - if (vibrator == null) { - logger.log(Level.FINE, "Vibrator Service not found."); + public void setView(GLSurfaceView view) { + pauseSensors(); + if (sensorManager != null) { + sensorManager.unregisterListener(this); + } + if (view == null) { + windowManager = null; + sensorManager = null; + } else { + // Get instance of the WindowManager from the current Context + windowManager = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE); + // Get instance of the SensorManager from the current Context + sensorManager = (SensorManager) view.getContext().getSystemService(Context.SENSOR_SERVICE); } } @@ -222,9 +198,6 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { unRegisterListener(entry.getKey()); } } - if (vibrator != null && vibratorActive) { - vibrator.cancel(); - } } /** @@ -400,10 +373,8 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { if (!sensorData.haveData) { sensorData.haveData = true; } else { - synchronized (eventQueue){ - if (axis.isChanged()) { - eventQueue.add(new JoyAxisEvent(axis, axis.getJoystickAxisValue())); - } + if (axis.isChanged()) { + joyHandler.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue())); } } } @@ -428,47 +399,14 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { // Start of JoyInput methods - 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; - } - } - - } - - public Joystick[] loadJoysticks(InputManager inputManager) { - this.inputManager = inputManager; - - initSensorManager(); - + public Joystick loadJoystick(int joyId, InputManager inputManager) { SensorData sensorData; - List list = new ArrayList(); - AndroidJoystick joystick; AndroidJoystickAxis axis; - joystick = new AndroidJoystick(inputManager, - this, - list.size(), + AndroidJoystick joystick = new AndroidJoystick(inputManager, + joyHandler, + joyId, "AndroidSensorsJoystick"); - list.add(joystick); List availSensors = sensorManager.getSensorList(Sensor.TYPE_ALL); for (Sensor sensor: availSensors) { @@ -555,14 +493,8 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { // } - joysticks = list.toArray( new AndroidJoystick[list.size()] ); loaded = true; - return joysticks; - } - - public void initialize() { - initialized = true; - loaded = false; + return joystick; } public void update() { @@ -570,15 +502,6 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { return; } updateOrientation(); - synchronized (eventQueue){ - // flush events to listener - if (listener != null && eventQueue.size() > 0) { - for (int i = 0; i < eventQueue.size(); i++){ - listener.onJoyAxisEvent(eventQueue.get(i)); - } - eventQueue.clear(); - } - } } public void destroy() { @@ -588,39 +511,27 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { sensorManager.unregisterListener(this); } sensors.clear(); - eventQueue.clear(); - initialized = false; loaded = false; - joysticks = null; sensorManager = null; - vibrator = null; - context = null; - } - - public boolean isInitialized() { - return initialized; - } - - public void setInputListener(RawInputListener listener) { - this.listener = listener; - } - - public long getInputTimeNanos() { - return System.nanoTime(); } - // End of JoyInput methods - // Start of Android SensorEventListener methods + @Override public void onSensorChanged(SensorEvent se) { - if (!initialized || !loaded) { + if (!loaded) { return; } + logger.log(Level.FINE, "onSensorChanged for {0}: accuracy: {1}, values: {2}", + new Object[]{se.sensor.getName(), se.accuracy, se.values}); int sensorType = se.sensor.getType(); SensorData sensorData = sensors.get(sensorType); + if (sensorData != null) { + logger.log(Level.FINE, "sensorData name: {0}, enabled: {1}, unreliable: {2}", + new Object[]{sensorData.sensor.getName(), sensorData.enabled, sensorData.sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE}); + } if (sensorData != null && sensorData.sensor.equals(se.sensor) && sensorData.enabled) { if (sensorData.sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) { @@ -641,10 +552,11 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { if (!sensorData.haveData) { sensorData.haveData = true; } else { - synchronized (eventQueue){ - if (axis.isChanged()) { - eventQueue.add(new JoyAxisEvent(axis, axis.getJoystickAxisValue())); - } + if (axis.isChanged()) { + JoyAxisEvent event = new JoyAxisEvent(axis, axis.getJoystickAxisValue()); + logger.log(Level.INFO, "adding JoyAxisEvent: {0}", event); + joyHandler.addEvent(event); +// joyHandler.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue())); } } } @@ -658,6 +570,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { } } + @Override public void onAccuracyChanged(Sensor sensor, int i) { int sensorType = sensor.getType(); SensorData sensorData = sensors.get(sensorType); @@ -697,7 +610,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { AndroidJoystickAxis axis; axis = new AndroidJoystickAxis( - inputManager, // InputManager (InputManager) + getInputManager(), // InputManager (InputManager) this, // parent Joystick (Joystick) axisNum, // Axis Index (int) axisName, // Axis Name (String) @@ -758,10 +671,12 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { this.maxRawValue = maxRawValue; } + @Override public float getMaxRawValue() { return maxRawValue; } + @Override public void setMaxRawValue(float maxRawValue) { this.maxRawValue = maxRawValue; } @@ -787,6 +702,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { return hasChanged; } + @Override public void calibrateCenter() { zeroRawValue = lastRawValue; logger.log(Level.FINE, "Calibrating axis {0} to {1}", diff --git a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java index cd849b1dd..5ef866188 100644 --- a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java +++ b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java @@ -46,17 +46,15 @@ import android.view.ViewGroup.LayoutParams; import android.widget.EditText; import android.widget.FrameLayout; import com.jme3.input.*; -import com.jme3.input.android.AndroidSensorJoyInput; import com.jme3.input.android.AndroidInputHandler; +import com.jme3.input.android.AndroidJoyInputHandler; import com.jme3.input.controls.SoftTextDialogInputListener; import com.jme3.input.dummy.DummyKeyInput; import com.jme3.input.dummy.DummyMouseInput; import com.jme3.renderer.android.AndroidGL; import com.jme3.renderer.opengl.GL; -import com.jme3.renderer.opengl.GLDebugES; import com.jme3.renderer.opengl.GLExt; import com.jme3.renderer.opengl.GLRenderer; -import com.jme3.renderer.opengl.GLTracer; import com.jme3.system.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; @@ -77,9 +75,9 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex protected SystemListener listener; protected boolean autoFlush = true; protected AndroidInputHandler androidInput; + protected AndroidJoyInputHandler androidJoyInput = null; protected long minFrameDuration = 0; // No FPS cap protected long lastUpdateTime = 0; - protected JoyInput androidSensorJoyInput = null; public OGLESContext() { } @@ -119,6 +117,12 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex androidInput.setView(view); androidInput.loadSettings(settings); + if (androidJoyInput == null) { + androidJoyInput = new AndroidJoyInputHandler(); + } + androidJoyInput.setView(view); + androidJoyInput.loadSettings(settings); + // setEGLContextClientVersion must be set before calling setRenderer // this means it cannot be set in AndroidConfigChooser (too late) view.setEGLContextClientVersion(2); @@ -231,6 +235,9 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex if (androidInput != null) { androidInput.loadSettings(settings); } + if (androidJoyInput != null) { + androidJoyInput.loadSettings(settings); + } if (settings.getFrameRate() > 0) { minFrameDuration = (long)(1000d / (double)settings.getFrameRate()); // ms @@ -267,10 +274,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex @Override public JoyInput getJoyInput() { - if (androidSensorJoyInput == null) { - androidSensorJoyInput = new AndroidSensorJoyInput(); - } - return androidSensorJoyInput; + return androidJoyInput; } @Override