1.1 --- a/android-project/project.properties Tue Mar 19 22:25:02 2013 -0700
1.2 +++ b/android-project/project.properties Tue Mar 19 23:03:57 2013 -0700
1.3 @@ -11,4 +11,4 @@
1.4 #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
1.5
1.6 # Project target.
1.7 -target=android-10
1.8 +target=android-12
2.1 --- a/android-project/src/org/libsdl/app/SDLActivity.java Tue Mar 19 22:25:02 2013 -0700
2.2 +++ b/android-project/src/org/libsdl/app/SDLActivity.java Tue Mar 19 23:03:57 2013 -0700
2.3 @@ -24,6 +24,8 @@
2.4 import android.content.*;
2.5
2.6 import java.lang.*;
2.7 +import java.util.List;
2.8 +import java.util.ArrayList;
2.9
2.10
2.11 /**
2.12 @@ -42,6 +44,11 @@
2.13
2.14 // This is what SDL runs in. It invokes SDL_main(), eventually
2.15 private static Thread mSDLThread;
2.16 +
2.17 + // Joystick
2.18 + private static List<Integer> mJoyIdList;
2.19 + // TODO: Have a (somewhat) more efficient way of storing these?
2.20 + private static List<List<Integer>> mJoyAxesLists;
2.21
2.22 // Audio
2.23 private static Thread mAudioThread;
2.24 @@ -156,12 +163,15 @@
2.25 public static native void nativePause();
2.26 public static native void nativeResume();
2.27 public static native void onNativeResize(int x, int y, int format);
2.28 + public static native void onNativePadDown(int padId, int keycode);
2.29 + public static native void onNativePadUp(int padId, int keycode);
2.30 + public static native void onNativeJoy(int joyId, int axisNum, float value);
2.31 public static native void onNativeKeyDown(int keycode);
2.32 public static native void onNativeKeyUp(int keycode);
2.33 public static native void onNativeTouch(int touchDevId, int pointerFingerId,
2.34 int action, float x,
2.35 float y, float p);
2.36 - public static native void onNativeAccel(float x, float y, float z);
2.37 +// public static native void onNativeAccel(float x, float y, float z);
2.38 public static native void nativeRunAudioThread();
2.39
2.40
2.41 @@ -180,6 +190,74 @@
2.42 mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title);
2.43 }
2.44
2.45 + // Call when initializing the joystick subsystem
2.46 + public static void joystickInit() {
2.47 + mJoyIdList = new ArrayList<Integer>();
2.48 + mJoyAxesLists = new ArrayList<List<Integer>>();
2.49 +
2.50 + int[] deviceIds = InputDevice.getDeviceIds();
2.51 + for(int i=0; i<deviceIds.length; i++) {
2.52 + if( (InputDevice.getDevice(deviceIds[i]).getSources() & 0x00000010 /* API 12: InputDevice.SOURCE_CLASS_JOYSTICK*/) != 0) {
2.53 + mJoyIdList.add(deviceIds[i]);
2.54 + List<Integer> axesList = new ArrayList<Integer>();
2.55 + /* With API 12 and above we can get a list of all motion
2.56 + * ranges, hence all axes. Some of them may be irrelevant
2.57 + * (e.g. an embedded trackpad). We filter the desired axes.
2.58 + */
2.59 + if(Build.VERSION.SDK_INT >= 12) {
2.60 + List<InputDevice.MotionRange> rangesList = InputDevice.getDevice(deviceIds[i]).getMotionRanges();
2.61 + for (InputDevice.MotionRange range : rangesList) {
2.62 + // Skip any possibly unrelated axis
2.63 + if ( (range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
2.64 + axesList.add(range.getAxis());
2.65 + }
2.66 + }
2.67 + } else {
2.68 + // In older versions, we can assume a sane X-Y default configuration
2.69 + axesList.add(MotionEvent.AXIS_X);
2.70 + axesList.add(MotionEvent.AXIS_Y);
2.71 + }
2.72 + mJoyAxesLists.add(axesList);
2.73 + }
2.74 + }
2.75 + }
2.76 +
2.77 + // Call when one clears joystick subsystem resources
2.78 + public static void joystickQuit() {
2.79 + mJoyIdList = null;
2.80 + mJoyAxesLists = null;
2.81 + }
2.82 +
2.83 + public static int getNumJoysticks() {
2.84 + if (mJoyIdList == null)
2.85 + return -1;
2.86 + return mJoyIdList.size();
2.87 + }
2.88 +
2.89 + public static String getJoystickName(int joy) {
2.90 + if (mJoyIdList == null)
2.91 + return null;
2.92 + return InputDevice.getDevice(mJoyIdList.get(joy)).getName();
2.93 + }
2.94 +
2.95 + public static List<Integer> getJoystickAxesList(int joy) {
2.96 + if (mJoyIdList == null)
2.97 + return null;
2.98 + return mJoyAxesLists.get(joy);
2.99 + }
2.100 +
2.101 + public static int getJoystickNumOfAxes(int joy) {
2.102 + if (mJoyIdList == null)
2.103 + return -1;
2.104 + return mJoyAxesLists.get(joy).size();
2.105 + }
2.106 +
2.107 + public static int getJoyId(int devId) {
2.108 + if (mJoyIdList == null)
2.109 + return -1;
2.110 + return mJoyIdList.indexOf(devId);
2.111 + }
2.112 +
2.113 public static void sendMessage(int command, int param) {
2.114 mSingleton.sendCommand(command, Integer.valueOf(param));
2.115 }
2.116 @@ -478,7 +556,12 @@
2.117 setFocusableInTouchMode(true);
2.118 requestFocus();
2.119 setOnKeyListener(this);
2.120 - setOnTouchListener(this);
2.121 + setOnTouchListener(this);
2.122 +
2.123 + // Listen to joystick motion events if supported
2.124 + if (Build.VERSION.SDK_INT >= 12) {
2.125 + setOnGenericMotionListener(new SDLOnGenericMotionListener());
2.126 + }
2.127
2.128 mSensorManager = (SensorManager)context.getSystemService("sensor");
2.129
2.130 @@ -568,18 +651,65 @@
2.131
2.132
2.133
2.134 + // Listen to joystick motion events if supported (API >= 12)
2.135 + private static class SDLOnGenericMotionListener implements View.OnGenericMotionListener {
2.136 + @Override
2.137 + public boolean onGenericMotion(View view, MotionEvent event) {
2.138 + int actionPointerIndex = event.getActionIndex();
2.139 + int action = event.getActionMasked();
2.140 +
2.141 + if ( (event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
2.142 + switch(action) {
2.143 + case MotionEvent.ACTION_MOVE:
2.144 + int id = SDLActivity.getJoyId( event.getDeviceId() );
2.145 + // The joystick subsystem may be uninitialized, so ignore
2.146 + if (id < 0)
2.147 + return true;
2.148 + // Update values for all joystick axes
2.149 + List<Integer> axes = SDLActivity.getJoystickAxesList(id);
2.150 + for (int axisIndex = 0; axisIndex < axes.size(); axisIndex++) {
2.151 + SDLActivity.onNativeJoy(id, axisIndex, event.getAxisValue(axes.get(axisIndex), actionPointerIndex));
2.152 + }
2.153 +
2.154 + return true;
2.155 + }
2.156 + }
2.157 + return false;
2.158 + }
2.159 + }
2.160 +
2.161 // Key events
2.162 public boolean onKey(View v, int keyCode, KeyEvent event) {
2.163 -
2.164 - if (event.getAction() == KeyEvent.ACTION_DOWN) {
2.165 - //Log.v("SDL", "key down: " + keyCode);
2.166 - SDLActivity.onNativeKeyDown(keyCode);
2.167 - return true;
2.168 - }
2.169 - else if (event.getAction() == KeyEvent.ACTION_UP) {
2.170 - //Log.v("SDL", "key up: " + keyCode);
2.171 - SDLActivity.onNativeKeyUp(keyCode);
2.172 - return true;
2.173 + /* Dispatch the different events depending on where they come from:
2.174 + * If the input device has some joystick source (probably differing
2.175 + * from the source to which the given key belongs), assume it is a
2.176 + * game controller button. Otherwise, assume a keyboard key.
2.177 + * This should also take care of some kinds of manually toggled soft
2.178 + * keyboards (i.e. not via the SDL text input API).
2.179 + */
2.180 + if ( (event.getDevice().getSources() & 0x00000010 /* API 12: InputDevice.SOURCE_CLASS_JOYSTICK*/) != 0) {
2.181 + int id = SDLActivity.getJoyId( event.getDeviceId() );
2.182 + // The joystick subsystem may be uninitialized, so ignore
2.183 + if (id < 0)
2.184 + return true;
2.185 + if (event.getAction() == KeyEvent.ACTION_DOWN) {
2.186 + SDLActivity.onNativePadDown(id, keyCode);
2.187 + return true;
2.188 + } else if (event.getAction() == KeyEvent.ACTION_UP) {
2.189 + SDLActivity.onNativePadUp(id, keyCode);
2.190 + return true;
2.191 + }
2.192 + } else {
2.193 + if (event.getAction() == KeyEvent.ACTION_DOWN) {
2.194 + //Log.v("SDL", "key down: " + keyCode);
2.195 + SDLActivity.onNativeKeyDown(keyCode);
2.196 + return true;
2.197 + }
2.198 + else if (event.getAction() == KeyEvent.ACTION_UP) {
2.199 + //Log.v("SDL", "key up: " + keyCode);
2.200 + SDLActivity.onNativeKeyUp(keyCode);
2.201 + return true;
2.202 + }
2.203 }
2.204
2.205 return false;
2.206 @@ -614,7 +744,7 @@
2.207 }
2.208 }
2.209 return true;
2.210 - }
2.211 + }
2.212
2.213 // Sensor events
2.214 public void enableSensor(int sensortype, boolean enabled) {
2.215 @@ -634,13 +764,14 @@
2.216 }
2.217
2.218 public void onSensorChanged(SensorEvent event) {
2.219 +/*
2.220 if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
2.221 SDLActivity.onNativeAccel(event.values[0] / SensorManager.GRAVITY_EARTH,
2.222 event.values[1] / SensorManager.GRAVITY_EARTH,
2.223 event.values[2] / SensorManager.GRAVITY_EARTH);
2.224 }
2.225 +*/
2.226 }
2.227 -
2.228 }
2.229
2.230 /* This is a fake invisible editor view that receives the input and defines the
3.1 --- a/src/core/android/SDL_android.cpp Tue Mar 19 22:25:02 2013 -0700
3.2 +++ b/src/core/android/SDL_android.cpp Tue Mar 19 23:03:57 2013 -0700
3.3 @@ -31,11 +31,15 @@
3.4
3.5 extern "C" {
3.6 #include "../../events/SDL_events_c.h"
3.7 +#include "../../joystick/android/SDL_androidjoystick.h"
3.8 #include "../../video/android/SDL_androidkeyboard.h"
3.9 #include "../../video/android/SDL_androidtouch.h"
3.10 #include "../../video/android/SDL_androidvideo.h"
3.11
3.12 #include <android/log.h>
3.13 +#if ENABLE_ACCELOMETER_AS_EMULATED_JOYSTICK
3.14 +#include <android/sensor.h>
3.15 +#endif
3.16 #include <pthread.h>
3.17 #include <sys/types.h>
3.18 #include <unistd.h>
3.19 @@ -76,9 +80,11 @@
3.20 static jmethodID midAudioWriteByteBuffer;
3.21 static jmethodID midAudioQuit;
3.22
3.23 +#ifdef ENABLE_ACCELOMETER_AS_EMULATED_JOYSTICK
3.24 // Accelerometer data storage
3.25 static float fLastAccelerometer[3];
3.26 static bool bHasNewData;
3.27 +#endif
3.28
3.29 /*******************************************************************************
3.30 Functions called by JNI
3.31 @@ -130,7 +136,9 @@
3.32 midAudioQuit = mEnv->GetStaticMethodID(mActivityClass,
3.33 "audioQuit", "()V");
3.34
3.35 +#ifdef ENABLE_ACCELOMETER_AS_EMULATED_JOYSTICK
3.36 bHasNewData = false;
3.37 +#endif
3.38
3.39 if(!midCreateGLContext || !midFlipBuffers || !midAudioInit ||
3.40 !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit) {
3.41 @@ -147,6 +155,27 @@
3.42 Android_SetScreenResolution(width, height, format);
3.43 }
3.44
3.45 +// Paddown
3.46 +extern "C" void Java_org_libsdl_app_SDLActivity_onNativePadDown(
3.47 + JNIEnv* env, jclass jcls, jint padId, jint keycode)
3.48 +{
3.49 + Android_OnPadDown(padId, keycode);
3.50 +}
3.51 +
3.52 +// Padup
3.53 +extern "C" void Java_org_libsdl_app_SDLActivity_onNativePadUp(
3.54 + JNIEnv* env, jclass jcls, jint padId, jint keycode)
3.55 +{
3.56 + Android_OnPadUp(padId, keycode);
3.57 +}
3.58 +
3.59 +// Joysticks
3.60 +extern "C" void Java_org_libsdl_app_SDLActivity_onNativeJoy(
3.61 + JNIEnv* env, jclass jcls, jint joyId, jint axisNum, jfloat value)
3.62 +{
3.63 + Android_OnJoy(joyId, axisNum, value);
3.64 +}
3.65 +
3.66 // Keydown
3.67 extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyDown(
3.68 JNIEnv* env, jclass jcls, jint keycode)
3.69 @@ -170,6 +199,7 @@
3.70 Android_OnTouch(touch_device_id_in, pointer_finger_id_in, action, x, y, p);
3.71 }
3.72
3.73 +#if ENABLE_ACCELOMETER_AS_EMULATED_JOYSTICK
3.74 // Accelerometer
3.75 extern "C" void Java_org_libsdl_app_SDLActivity_onNativeAccel(
3.76 JNIEnv* env, jclass jcls,
3.77 @@ -180,6 +210,7 @@
3.78 fLastAccelerometer[2] = z;
3.79 bHasNewData = true;
3.80 }
3.81 +#endif
3.82
3.83 // Quit
3.84 extern "C" void Java_org_libsdl_app_SDLActivity_nativeQuit(
3.85 @@ -347,6 +378,7 @@
3.86 }
3.87 }
3.88
3.89 +#if ENABLE_ACCELOMETER_AS_EMULATED_JOYSTICK
3.90 extern "C" SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
3.91 {
3.92 int i;
3.93 @@ -362,6 +394,7 @@
3.94
3.95 return retval;
3.96 }
3.97 +#endif
3.98
3.99 static void Android_JNI_ThreadDestroyed(void* value) {
3.100 /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
3.101 @@ -1082,6 +1115,101 @@
3.102 return 0;
3.103 }
3.104
3.105 +// Initialize the joystick subsystem on the Java side
3.106 +int Android_JNI_JoystickInit()
3.107 +{
3.108 + JNIEnv* env = Android_JNI_GetEnv();
3.109 + if (!env) {
3.110 + return -1;
3.111 + }
3.112 + jmethodID mid = env->GetStaticMethodID(mActivityClass, "joystickInit", "()V");
3.113 + if (!mid) {
3.114 + return -1;
3.115 + }
3.116 + env->CallStaticVoidMethod(mActivityClass, mid);
3.117 + return 0;
3.118 +}
3.119 +
3.120 +// Quit the joystick subsystem on the Java side
3.121 +int Android_JNI_JoystickQuit()
3.122 +{
3.123 + JNIEnv* env = Android_JNI_GetEnv();
3.124 + if (!env) {
3.125 + return -1;
3.126 + }
3.127 + jmethodID mid = env->GetStaticMethodID(mActivityClass, "joystickQuit", "()V");
3.128 + if (!mid) {
3.129 + return -1;
3.130 + }
3.131 + env->CallStaticVoidMethod(mActivityClass, mid);
3.132 + return 0;
3.133 +}
3.134 +
3.135 +// return the total number of plugged in joysticks
3.136 +extern "C" int Android_JNI_GetNumJoysticks()
3.137 +{
3.138 + JNIEnv* env = Android_JNI_GetEnv();
3.139 + if (!env) {
3.140 + return -1;
3.141 + }
3.142 + jmethodID mid = env->GetStaticMethodID(mActivityClass, "getNumJoysticks", "()I");
3.143 + if (!mid) {
3.144 + return -1;
3.145 + }
3.146 +
3.147 + return env->CallStaticIntMethod(mActivityClass, mid);
3.148 +}
3.149 +
3.150 +// Return the name of joystick number "index"
3.151 +extern "C" char* Android_JNI_GetJoystickName(int index)
3.152 +{
3.153 + JNIEnv* env = Android_JNI_GetEnv();
3.154 + if (!env) {
3.155 + return SDL_strdup("");
3.156 + }
3.157 +
3.158 + jmethodID mid = env->GetStaticMethodID(mActivityClass, "getJoystickName", "(I)Ljava/lang/String;");
3.159 + if (!mid) {
3.160 + return SDL_strdup("");
3.161 + }
3.162 + jstring string = reinterpret_cast<jstring>(env->CallStaticObjectMethod(mActivityClass, mid, index));
3.163 + const char* utf = env->GetStringUTFChars(string, 0);
3.164 + if (!utf) {
3.165 + return SDL_strdup("");
3.166 + }
3.167 +
3.168 + char* text = SDL_strdup(utf);
3.169 + env->ReleaseStringUTFChars(string, utf);
3.170 + return text;
3.171 +}
3.172 +
3.173 +// return the number of axes in the given joystick
3.174 +extern "C" int Android_JNI_GetJoystickNumOfAxes(int index)
3.175 +{
3.176 + JNIEnv* env = Android_JNI_GetEnv();
3.177 + if (!env) {
3.178 + return -1;
3.179 + }
3.180 + jmethodID mid = env->GetStaticMethodID(mActivityClass, "getJoystickNumOfAxes", "(I)I");
3.181 + if (!mid) {
3.182 + return -1;
3.183 + }
3.184 +
3.185 + return env->CallStaticIntMethod(mActivityClass, mid, index);
3.186 +}
3.187 +
3.188 +#if ENABLE_ACCELOMETER_AS_EMULATED_JOYSTICK
3.189 +// Return the name of the default accelerometer
3.190 +// This is much easier to be done with NDK than with JNI
3.191 +extern "C" char* Android_GetAccelName()
3.192 +{
3.193 + ASensorManager* mSensorManager = ASensorManager_getInstance();
3.194 + ASensor const* mAccelerometer = ASensorManager_getDefaultSensor(mSensorManager, ASENSOR_TYPE_ACCELEROMETER);
3.195 +
3.196 + return SDL_strdup(ASensor_getName(mAccelerometer));
3.197 +}
3.198 +#endif
3.199 +
3.200 // sends message to be handled on the UI event dispatch thread
3.201 extern "C" int Android_JNI_SendMessage(int command, int param)
3.202 {
4.1 --- a/src/core/android/SDL_android.h Tue Mar 19 22:25:02 2013 -0700
4.2 +++ b/src/core/android/SDL_android.h Tue Mar 19 23:03:57 2013 -0700
4.3 @@ -33,7 +33,9 @@
4.4 extern SDL_bool Android_JNI_CreateContext(int majorVersion, int minorVersion, int red, int green, int blue, int alpha, int buffer, int depth, int stencil, int buffers, int samples);
4.5 extern void Android_JNI_SwapWindow();
4.6 extern void Android_JNI_SetActivityTitle(const char *title);
4.7 +#ifdef ENABLE_ACCELOMETER_AS_EMULATED_JOYSTICK
4.8 extern SDL_bool Android_JNI_GetAccelerometerValues(float values[3]);
4.9 +#endif
4.10 extern void Android_JNI_ShowTextInput(SDL_Rect *inputRect);
4.11 extern void Android_JNI_HideTextInput();
4.12
4.13 @@ -60,6 +62,16 @@
4.14 /* Power support */
4.15 int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent);
4.16
4.17 +/* Joystick/accelerometer support */
4.18 +int Android_JNI_JoystickInit();
4.19 +int Android_JNI_JoystickQuit();
4.20 +int Android_JNI_GetNumJoysticks();
4.21 +char* Android_JNI_GetJoystickName(int i);
4.22 +int Android_JNI_GetJoystickNumOfAxes(int index);
4.23 +#ifdef ENABLE_ACCELOMETER_AS_EMULATED_JOYSTICK
4.24 +char* Android_GetAccelName();
4.25 +#endif
4.26 +
4.27 // Threads
4.28 #include <jni.h>
4.29 static void Android_JNI_ThreadDestroyed(void*);