diff --git a/README-android.txt b/README-android.txt
index a161c8407..4a1cfc29f 100644
--- a/README-android.txt
+++ b/README-android.txt
@@ -4,13 +4,14 @@ Simple DirectMedia Layer for Android
Requirements:
-Android SDK (version 10 or later)
+Android SDK (version 12 or later)
http://developer.android.com/sdk/index.html
Android NDK r7 or later
http://developer.android.com/sdk/ndk/index.html
Minimum API level supported by SDL: 10 (Android 2.3.3)
+Joystick support is available for API level >=12 devices.
================================================================================
How the port works
@@ -396,8 +397,11 @@ When you're done instrumenting with valgrind, you can disable the wrapper:
Why is API level 10 the minimum required?
================================================================================
-API level 10 is required because SDL requires some functionality for running not
-available on older devices and some for building which is not in older NDK/SDKs.
+API level 10 is the minimum required level at runtime (that is, on the device)
+because SDL requires some functionality for running not
+available on older devices. Since the incorporation of joystick support into SDL,
+the minimum SDK required to *build* SDL is version 12. Devices running API levels
+10-11 are still supported, only with the joystick functionality disabled.
Support for native OpenGL ES and ES2 applications was introduced in the NDK for
API level 4 and 8. EGL was made a stable API in the NDK for API level 9, which
diff --git a/WhatsNew.txt b/WhatsNew.txt
index 5ac78c513..36afc564c 100644
--- a/WhatsNew.txt
+++ b/WhatsNew.txt
@@ -1,6 +1,16 @@
This is a list of major changes in SDL's version history.
+---------------------------------------------------------------------------
+2.0.2:
+---------------------------------------------------------------------------
+
+Android:
+* Joystick support (minimum SDK version required to build SDL is now 12,
+ the required runtime version remains at 10, but on such devices joystick
+ support won't be available).
+
+
---------------------------------------------------------------------------
2.0.1:
---------------------------------------------------------------------------
diff --git a/android-project/AndroidManifest.xml b/android-project/AndroidManifest.xml
index 27db41843..0e4ad4162 100644
--- a/android-project/AndroidManifest.xml
+++ b/android-project/AndroidManifest.xml
@@ -31,7 +31,7 @@
-
+
diff --git a/android-project/default.properties b/android-project/default.properties
index 9d135cb85..0cdab9561 100644
--- a/android-project/default.properties
+++ b/android-project/default.properties
@@ -8,4 +8,4 @@
# project structure.
# Project target.
-target=android-7
+target=android-12
diff --git a/android-project/jni/Application.mk b/android-project/jni/Application.mk
index 05cf0c31c..e5b50793b 100644
--- a/android-project/jni/Application.mk
+++ b/android-project/jni/Application.mk
@@ -2,3 +2,5 @@
# Uncomment this if you're using STL in your project
# See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information
# APP_STL := stlport_static
+
+APP_ABI := armeabi armeabi-v7a x86
diff --git a/android-project/project.properties b/android-project/project.properties
index b7c2081d5..0f507e530 100644
--- a/android-project/project.properties
+++ b/android-project/project.properties
@@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
-target=android-10
+target=android-12
diff --git a/android-project/src/org/libsdl/app/SDLActivity.java b/android-project/src/org/libsdl/app/SDLActivity.java
index 49b014ce1..f7cb9abca 100644
--- a/android-project/src/org/libsdl/app/SDLActivity.java
+++ b/android-project/src/org/libsdl/app/SDLActivity.java
@@ -16,6 +16,10 @@ import android.graphics.*;
import android.media.*;
import android.hardware.*;
+import java.lang.*;
+import java.util.List;
+import java.util.ArrayList;
+
/**
SDL Activity
@@ -31,10 +35,11 @@ public class SDLActivity extends Activity {
protected static SDLSurface mSurface;
protected static View mTextEdit;
protected static ViewGroup mLayout;
+ protected static SDLJoystickHandler mJoystickHandler;
// This is what SDL runs in. It invokes SDL_main(), eventually
protected static Thread mSDLThread;
-
+
// Audio
protected static Thread mAudioThread;
protected static AudioTrack mAudioTrack;
@@ -60,6 +65,13 @@ public class SDLActivity extends Activity {
// Set up the surface
mSurface = new SDLSurface(getApplication());
+
+ if(Build.VERSION.SDK_INT >= 12) {
+ mJoystickHandler = new SDLJoystickHandler_API12();
+ }
+ else {
+ mJoystickHandler = new SDLJoystickHandler();
+ }
mLayout = new AbsoluteLayout(this);
mLayout.addView(mSurface);
@@ -236,6 +248,10 @@ public class SDLActivity extends Activity {
public static native void nativePause();
public static native void nativeResume();
public static native void onNativeResize(int x, int y, int format);
+ public static native void onNativePadDown(int padId, int keycode);
+ public static native void onNativePadUp(int padId, int keycode);
+ public static native void onNativeJoy(int joyId, int axis,
+ float value);
public static native void onNativeKeyDown(int keycode);
public static native void onNativeKeyUp(int keycode);
public static native void onNativeKeyboardFocusLost();
@@ -406,6 +422,23 @@ public class SDLActivity extends Activity {
}
return Arrays.copyOf(filtered, used);
}
+
+ // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
+ public static int getNumJoysticks() {
+ return mJoystickHandler.getNumJoysticks();
+ }
+
+ public static String getJoystickName(int joy) {
+ return mJoystickHandler.getJoystickName(joy);
+ }
+
+ public static int getJoystickAxes(int joy) {
+ return mJoystickHandler.getJoystickAxes(joy);
+ }
+
+ public static int getJoyId(int devId) {
+ return mJoystickHandler.getJoyId(devId);
+ }
}
/**
@@ -451,6 +484,10 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+
+ if(Build.VERSION.SDK_INT >= 12) {
+ setOnGenericMotionListener(new SDLGenericMotionHandler_API12());
+ }
// Some arbitrary defaults to avoid a potential division by zero
mWidth = 1.0f;
@@ -557,16 +594,26 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
// Key events
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
-
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- //Log.v("SDL", "key down: " + keyCode);
- SDLActivity.onNativeKeyDown(keyCode);
- return true;
- }
- else if (event.getAction() == KeyEvent.ACTION_UP) {
- //Log.v("SDL", "key up: " + keyCode);
- SDLActivity.onNativeKeyUp(keyCode);
- return true;
+ // Dispatch the different events depending on where they come from
+ if(event.getSource() == InputDevice.SOURCE_KEYBOARD) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ //Log.v("SDL", "key down: " + keyCode);
+ SDLActivity.onNativeKeyDown(keyCode);
+ return true;
+ }
+ else if (event.getAction() == KeyEvent.ACTION_UP) {
+ //Log.v("SDL", "key up: " + keyCode);
+ SDLActivity.onNativeKeyUp(keyCode);
+ return true;
+ }
+ } else if ( (event.getSource() & 0x00000401) != 0 || /* API 12: SOURCE_GAMEPAD */
+ (event.getSource() & InputDevice.SOURCE_DPAD) != 0 ) {
+ int id = SDLActivity.getJoyId( event.getDeviceId() );
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ SDLActivity.onNativePadDown(id, keyCode);
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ SDLActivity.onNativePadUp(id, keyCode);
+ }
}
return false;
@@ -646,8 +693,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
y / SensorManager.GRAVITY_EARTH,
event.values[2] / SensorManager.GRAVITY_EARTH - 1);
}
- }
-
+ }
}
/* This is a fake invisible editor view that receives the input and defines the
@@ -769,3 +815,95 @@ class SDLInputConnection extends BaseInputConnection {
}
+/* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
+class SDLJoystickHandler {
+ public int getNumJoysticks() {
+ return 0;
+ }
+
+ public String getJoystickName(int joy) {
+ return "";
+ }
+
+ public int getJoystickAxes(int joy) {
+ return 0;
+ }
+
+ public int getJoyId(int devId) {
+ return 0;
+ }
+}
+
+/* Actual joystick functionality available for API >= 12 devices */
+class SDLJoystickHandler_API12 extends SDLJoystickHandler {
+ private List mJoyIdList;
+
+ // Create a list of valid ID's the first time this function is called
+ private void createJoystickList() {
+ if(mJoyIdList != null) {
+ return;
+ }
+
+ mJoyIdList = new ArrayList();
+ int[] deviceIds = InputDevice.getDeviceIds();
+ for(int i=0; iGetStaticMethodID(env, mActivityClass, "getNumJoysticks", "()I");
+ if (!mid) {
+ return -1;
+ }
+
+ return (int)(*env)->CallStaticIntMethod(env, mActivityClass, mid);
+}
+
+/* Return the name of joystick number "i" */
+char* Android_JNI_GetJoystickName(int i)
+{
+ JNIEnv* env = Android_JNI_GetEnv();
+ if (!env) {
+ return SDL_strdup("");
+ }
+
+ jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "getJoystickName", "(I)Ljava/lang/String;");
+ if (!mid) {
+ return SDL_strdup("");
+ }
+ jstring string = (jstring)((*env)->CallStaticObjectMethod(env, mActivityClass, mid, i));
+ const char* utf = (*env)->GetStringUTFChars(env, string, 0);
+ if (!utf) {
+ return SDL_strdup("");
+ }
+
+ char* text = SDL_strdup(utf);
+ (*env)->ReleaseStringUTFChars(env, string, utf);
+ return text;
+}
+
+/* return the number of axes in the given joystick */
+int Android_JNI_GetJoystickAxes(int joy)
+{
+ JNIEnv* env = Android_JNI_GetEnv();
+ if (!env) {
+ return -1;
+ }
+
+ jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "getJoystickAxes", "(I)I");
+ if (!mid) {
+ return -1;
+ }
+
+ return (int)(*env)->CallIntMethod(env, mActivityClass, mid, joy);
+}
+
+
/* sends message to be handled on the UI event dispatch thread */
int Android_JNI_SendMessage(int command, int param)
{
diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h
index 6181a15db..805518a8f 100644
--- a/src/core/android/SDL_android.h
+++ b/src/core/android/SDL_android.h
@@ -64,6 +64,11 @@ SDL_bool Android_JNI_HasClipboardText();
/* Power support */
int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent);
+
+/* Joystick support */
+int Android_JNI_GetNumJoysticks();
+char* Android_JNI_GetJoystickName(int i);
+int Android_JNI_GetJoystickAxes(int joy);
/* Touch support */
int Android_JNI_GetTouchDeviceIds(int **ids);
diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c
index adaca528e..0c7f81001 100644
--- a/src/joystick/android/SDL_sysjoystick.c
+++ b/src/joystick/android/SDL_sysjoystick.c
@@ -29,11 +29,48 @@
#include "SDL_error.h"
#include "SDL_events.h"
#include "SDL_joystick.h"
+#include "SDL_hints.h"
#include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h"
#include "../../core/android/SDL_android.h"
-static const char *accelerometerName = "Android accelerometer";
+#define ANDROID_ACCELEROMETER_INDEX (SYS_numjoysticks - 1)
+#define ANDROID_ACCELEROMETER_NAME "Android Accelerometer"
+
+static SDL_Joystick **SYS_Joysticks;
+static char **SYS_JoystickNames;
+static int SYS_numjoysticks;
+static SDL_bool SYS_accelAsJoy;
+
+/* Function to convert Android keyCodes into SDL ones.
+ * This code manipulation is done to get a sequential list of codes.
+ */
+int
+keycode_to_SDL(int keycode)
+{
+ int final = 0;
+ /* D-Pad key codes (API 1):
+ * KEYCODE_DPAD_UP=19, KEYCODE_DPAD_DOWN
+ * KEYCODE_DPAD_LEFT, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_CENTER
+ */
+ if(keycode < 96)
+ return keycode-19;
+ /* Some gamepad buttons (API 9):
+ * KEYCODE_BUTTON_A=96, KEYCODE_BUTTON_B, KEYCODE_BUTTON_C,
+ * KEYCODE_BUTTON_X, KEYCODE_BUTTON_Y, KEYCODE_BUTTON_Z,
+ * KEYCODE_BUTTON_L1, KEYCODE_BUTTON_L2,
+ * KEYCODE_BUTTON_R1, KEYCODE_BUTTON_R2,
+ * KEYCODE_BUTTON_THUMBL, KEYCODE_BUTTON_THUMBR,
+ * KEYCODE_BUTTON_START, KEYCODE_BUTTON_SELECT, KEYCODE_BUTTON_MODE
+ */
+ else if(keycode < 188)
+ return keycode-91;
+ /* More gamepad buttons (API 12):
+ * KEYCODE_BUTTON_1=188 to KEYCODE_BUTTON_16
+ */
+ else
+ return keycode-168;
+}
/* Function to scan the system for joysticks.
* This function should set SDL_numjoysticks to the number of available
@@ -43,18 +80,56 @@ static const char *accelerometerName = "Android accelerometer";
int
SDL_SYS_JoystickInit(void)
{
- return (1);
+ int i = 0;
+ const char *env;
+
+ env = SDL_GetHint(SDL_HINT_ACCEL_AS_JOY);
+ if (env && !SDL_atoi(env))
+ SYS_accelAsJoy = SDL_FALSE;
+ else
+ SYS_accelAsJoy = SDL_TRUE; /* Default behavior */
+
+ SYS_numjoysticks = Android_JNI_GetNumJoysticks();
+ if (SYS_accelAsJoy) {
+ SYS_numjoysticks++;
+ }
+ SYS_Joysticks = (SDL_Joystick **)SDL_malloc(SYS_numjoysticks*sizeof(SDL_Joystick *));
+ if (SYS_Joysticks == NULL)
+ {
+ return SDL_OutOfMemory();
+ }
+ SYS_JoystickNames = (char **)SDL_malloc(SYS_numjoysticks*sizeof(char *));
+ if (SYS_JoystickNames == NULL)
+ {
+ SDL_free(SYS_Joysticks);
+ SYS_Joysticks = NULL;
+ return SDL_OutOfMemory();
+ }
+ SDL_memset(SYS_JoystickNames, 0, (SYS_numjoysticks*sizeof(char *)));
+ SDL_memset(SYS_Joysticks, 0, (SYS_numjoysticks*sizeof(SDL_Joystick *)));
+
+ for (i = 0; i < SYS_numjoysticks; i++)
+ {
+ if ( SYS_accelAsJoy && i == ANDROID_ACCELEROMETER_INDEX ) {
+ SYS_JoystickNames[i] = ANDROID_ACCELEROMETER_NAME;
+ } else {
+ SYS_JoystickNames[i] = Android_JNI_GetJoystickName(i);
+ }
+ }
+
+ return (SYS_numjoysticks);
}
int SDL_SYS_NumJoysticks()
{
- return 1;
+ return SYS_numjoysticks;
}
void SDL_SYS_JoystickDetect()
{
}
+/* TODO: Hotplugging support */
SDL_bool SDL_SYS_JoystickNeedsPolling()
{
return SDL_FALSE;
@@ -64,7 +139,7 @@ SDL_bool SDL_SYS_JoystickNeedsPolling()
const char *
SDL_SYS_JoystickNameForDeviceIndex(int device_index)
{
- return accelerometerName;
+ return SYS_JoystickNames[device_index];
}
/* Function to perform the mapping from device index to the instance id for this index */
@@ -81,11 +156,19 @@ SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
int
SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
{
- if (device_index == 0) {
- joystick->nbuttons = 0;
+ if (device_index < SYS_numjoysticks) {
joystick->nhats = 0;
joystick->nballs = 0;
- joystick->naxes = 3;
+ if (SYS_accelAsJoy && device_index == ANDROID_ACCELEROMETER_INDEX) {
+ joystick->nbuttons = 0;
+ joystick->naxes = 3;
+ } else {
+ /* TODO: Get the real number of buttons in the device */
+ joystick->nbuttons = 36;
+ joystick->naxes = Android_JNI_GetJoystickAxes(device_index);
+ }
+
+ SYS_Joysticks[device_index] = joystick;
return 0;
} else {
SDL_SetError("No joystick available with that index");
@@ -111,7 +194,8 @@ SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
Sint16 value;
float values[3];
- if (Android_JNI_GetAccelerometerValues(values)) {
+ if (SYS_accelAsJoy && Android_JNI_GetAccelerometerValues(values) &&
+ joystick->instance_id == ANDROID_ACCELEROMETER_INDEX) {
for ( i = 0; i < 3; i++ ) {
value = (Sint16)(values[i] * 32767.0f);
SDL_PrivateJoystickAxis(joystick, i, value);
@@ -129,6 +213,10 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick)
void
SDL_SYS_JoystickQuit(void)
{
+ SDL_free(SYS_JoystickNames);
+ SDL_free(SYS_Joysticks);
+ SYS_JoystickNames = NULL;
+ SYS_Joysticks = NULL;
}
SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
@@ -151,6 +239,32 @@ SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
return guid;
}
+int
+Android_OnPadDown(int padId, int keycode)
+{
+ SDL_PrivateJoystickButton(SYS_Joysticks[padId], keycode_to_SDL(keycode), SDL_PRESSED);
+
+ return 0;
+}
+
+int
+Android_OnPadUp(int padId, int keycode)
+{
+ SDL_PrivateJoystickButton(SYS_Joysticks[padId], keycode_to_SDL(keycode), SDL_RELEASED);
+
+ return 0;
+}
+
+int
+Android_OnJoy(int joyId, int axis, float value)
+{
+ /* Android gives joy info normalized as [-1.0, 1.0] or [0.0, 1.0] */
+ /* TODO: Are the reported values right? */
+ SDL_PrivateJoystickAxis(SYS_Joysticks[joyId], axis, (Sint16) (32767.*value) );
+
+ return 0;
+}
+
#endif /* SDL_JOYSTICK_ANDROID */
/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/joystick/android/SDL_sysjoystick.h b/src/joystick/android/SDL_sysjoystick.h
new file mode 100644
index 000000000..f98c6a04b
--- /dev/null
+++ b/src/joystick/android/SDL_sysjoystick.h
@@ -0,0 +1,28 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2013 Sam Lantinga
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include "SDL_config.h"
+
+extern int Android_OnPadDown(int padId, int keycode);
+extern int Android_OnPadUp(int padId, int keycode);
+extern int Android_OnJoy(int joyId, int axisnum, float value);
+
+/* vi: set ts=4 sw=4 expandtab: */