Android: Removed all unnecessary dependencies on C++.
authorEric Wing <ewing . public |-at-| gmail . com>
Mon, 22 Jul 2013 02:51:45 -0700
changeset 7501 b27825bb5879
parent 7500 6b484ff38f86
child 7502 6ff02ff3cf06
Android: Removed all unnecessary dependencies on C++. C++ is a bit of a minefield on Android. Much functionality still doesn't work, and Android can't decide on which C++ standard library to use, so it provides 3 different ones, all of which are incompatible with each other. (It looks like clang is coming too which will add a new compiler and a 4th standard library.) As middleware, SDL might be distributed as a binary and intermixed with other projects already using C++. If C++ is intermixed in a bad way, bad things will happen. Removing dependencies on C++ will avoid this problem and downstream users won't have to worry/care.
Android.mk
README-android.txt
android-project/jni/src/Android.mk
src/core/android/SDL_android.c
src/core/android/SDL_android.cpp
src/main/android/SDL_android_main.c
src/main/android/SDL_android_main.cpp
--- a/Android.mk	Mon Jul 22 22:54:00 2013 +0200
+++ b/Android.mk	Mon Jul 22 02:51:45 2013 -0700
@@ -22,7 +22,7 @@
 	$(wildcard $(LOCAL_PATH)/src/audio/dummy/*.c) \
 	$(LOCAL_PATH)/src/atomic/SDL_atomic.c \
 	$(LOCAL_PATH)/src/atomic/SDL_spinlock.c.arm \
-	$(wildcard $(LOCAL_PATH)/src/core/android/*.cpp) \
+	$(wildcard $(LOCAL_PATH)/src/core/android/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/cpuinfo/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/events/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/file/*.c) \
--- a/README-android.txt	Mon Jul 22 22:54:00 2013 +0200
+++ b/README-android.txt	Mon Jul 22 02:51:45 2013 -0700
@@ -28,10 +28,10 @@
 
 The Java code loads your game code, the SDL shared library, and
 dispatches to native functions implemented in the SDL library:
-src/SDL_android.cpp
+src/SDL_android.c
 
 Your project must include some glue code that starts your main() routine:
-src/main/android/SDL_android_main.cpp
+src/main/android/SDL_android_main.c
 
 
 ================================================================================
--- a/android-project/jni/src/Android.mk	Mon Jul 22 22:54:00 2013 +0200
+++ b/android-project/jni/src/Android.mk	Mon Jul 22 02:51:45 2013 -0700
@@ -9,7 +9,7 @@
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include
 
 # Add your application source files here...
-LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.cpp \
+LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \
 	YourSourceHere.c
 
 LOCAL_SHARED_LIBRARIES := SDL2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/android/SDL_android.c	Mon Jul 22 02:51:45 2013 -0700
@@ -0,0 +1,1353 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
+
+  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"
+#include "SDL_stdinc.h"
+#include "SDL_assert.h"
+#include "SDL_log.h"
+
+#ifdef __ANDROID__
+
+#include "SDL_system.h"
+#include "SDL_android.h"
+#include <EGL/egl.h>
+
+#include "../../events/SDL_events_c.h"
+#include "../../video/android/SDL_androidkeyboard.h"
+#include "../../video/android/SDL_androidtouch.h"
+#include "../../video/android/SDL_androidvideo.h"
+
+#include <android/log.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <unistd.h>
+#define LOG_TAG "SDL_android"
+//#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
+//#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
+#define LOGI(...) do {} while (false)
+#define LOGE(...) do {} while (false)
+
+/* Uncomment this to log messages entering and exiting methods in this file */
+//#define DEBUG_JNI
+
+/* Implemented in audio/android/SDL_androidaudio.c */
+extern void Android_RunAudioThread();
+
+static void Android_JNI_ThreadDestroyed(void*);
+
+/*******************************************************************************
+ This file links the Java side of Android with libsdl
+*******************************************************************************/
+#include <jni.h>
+#include <android/log.h>
+#include <stdbool.h>
+
+
+/*******************************************************************************
+                               Globals
+*******************************************************************************/
+static pthread_key_t mThreadKey;
+static JavaVM* mJavaVM;
+
+// Main activity
+static jclass mActivityClass;
+
+// method signatures
+static jmethodID midCreateGLContext;
+static jmethodID midFlipBuffers;
+static jmethodID midAudioInit;
+static jmethodID midAudioWriteShortBuffer;
+static jmethodID midAudioWriteByteBuffer;
+static jmethodID midAudioQuit;
+
+// Accelerometer data storage
+static float fLastAccelerometer[3];
+static bool bHasNewData;
+
+/*******************************************************************************
+                 Functions called by JNI
+*******************************************************************************/
+
+// Library init
+jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+    JNIEnv *env;
+    mJavaVM = vm;
+    LOGI("JNI_OnLoad called");
+    if ((*mJavaVM)->GetEnv(mJavaVM, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        LOGE("Failed to get the environment using GetEnv()");
+        return -1;
+    }
+    /*
+     * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
+     * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
+     */
+    if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed)) {
+        __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing pthread key");
+    }
+    else {
+        Android_JNI_SetupThread();
+    }
+
+    return JNI_VERSION_1_4;
+}
+
+// Called before SDL_main() to initialize JNI bindings
+void SDL_Android_Init(JNIEnv* mEnv, jclass cls)
+{
+    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init()");
+
+    Android_JNI_SetupThread();
+
+    mActivityClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
+
+    midCreateGLContext = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "createGLContext","(II[I)Z");
+    midFlipBuffers = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "flipBuffers","()V");
+    midAudioInit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "audioInit", "(IZZI)V");
+    midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "audioWriteShortBuffer", "([S)V");
+    midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "audioWriteByteBuffer", "([B)V");
+    midAudioQuit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "audioQuit", "()V");
+
+    bHasNewData = false;
+
+    if(!midCreateGLContext || !midFlipBuffers || !midAudioInit ||
+       !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit) {
+        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
+    }
+    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init() finished!");
+}
+
+// Resize
+void Java_org_libsdl_app_SDLActivity_onNativeResize(
+                                    JNIEnv* env, jclass jcls,
+                                    jint width, jint height, jint format)
+{
+    Android_SetScreenResolution(width, height, format);
+}
+
+// Keydown
+void Java_org_libsdl_app_SDLActivity_onNativeKeyDown(
+                                    JNIEnv* env, jclass jcls, jint keycode)
+{
+    Android_OnKeyDown(keycode);
+}
+
+// Keyup
+void Java_org_libsdl_app_SDLActivity_onNativeKeyUp(
+                                    JNIEnv* env, jclass jcls, jint keycode)
+{
+    Android_OnKeyUp(keycode);
+}
+
+// Touch
+void Java_org_libsdl_app_SDLActivity_onNativeTouch(
+                                    JNIEnv* env, jclass jcls,
+                                    jint touch_device_id_in, jint pointer_finger_id_in,
+                                    jint action, jfloat x, jfloat y, jfloat p)
+{
+    Android_OnTouch(touch_device_id_in, pointer_finger_id_in, action, x, y, p);
+}
+
+// Accelerometer
+void Java_org_libsdl_app_SDLActivity_onNativeAccel(
+                                    JNIEnv* env, jclass jcls,
+                                    jfloat x, jfloat y, jfloat z)
+{
+    fLastAccelerometer[0] = x;
+    fLastAccelerometer[1] = y;
+    fLastAccelerometer[2] = z;
+    bHasNewData = true;
+}
+
+// Low memory
+void Java_org_libsdl_app_SDLActivity_nativeLowMemory(
+                                    JNIEnv* env, jclass cls)
+{
+    SDL_SendAppEvent(SDL_APP_LOWMEMORY);
+}
+
+// Quit
+void Java_org_libsdl_app_SDLActivity_nativeQuit(
+                                    JNIEnv* env, jclass cls)
+{
+    // Inject a SDL_QUIT event
+    SDL_SendQuit();
+    SDL_SendAppEvent(SDL_APP_TERMINATING);
+}
+
+// Pause
+void Java_org_libsdl_app_SDLActivity_nativePause(
+                                    JNIEnv* env, jclass cls)
+{
+    if (Android_Window) {
+        /* Signal the pause semaphore so the event loop knows to pause and (optionally) block itself */
+        if (!SDL_SemValue(Android_PauseSem)) SDL_SemPost(Android_PauseSem);
+        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
+        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
+    }
+
+    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
+    SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
+    SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
+}
+
+// Resume
+void Java_org_libsdl_app_SDLActivity_nativeResume(
+                                    JNIEnv* env, jclass cls)
+{
+    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()");
+    SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
+    SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
+
+    if (Android_Window) {
+        /* Signal the resume semaphore so the event loop knows to resume and restore the GL Context
+         * We can't restore the GL Context here because it needs to be done on the SDL main thread
+         * and this function will be called from the Java thread instead.
+         */
+        if (!SDL_SemValue(Android_ResumeSem)) SDL_SemPost(Android_ResumeSem);
+        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
+        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0);
+    }
+}
+
+void Java_org_libsdl_app_SDLActivity_nativeRunAudioThread(
+                                    JNIEnv* env, jclass cls)
+{
+    /* This is the audio thread, with a different environment */
+    Android_JNI_SetupThread();
+
+    Android_RunAudioThread();
+}
+
+void Java_org_libsdl_app_SDLInputConnection_nativeCommitText(
+                                    JNIEnv* env, jclass cls,
+                                    jstring text, jint newCursorPosition)
+{
+    const char *utftext = (*env)->GetStringUTFChars(env, text, NULL);
+
+    SDL_SendKeyboardText(utftext);
+
+    (*env)->ReleaseStringUTFChars(env, text, utftext);
+}
+
+void Java_org_libsdl_app_SDLInputConnection_nativeSetComposingText(
+                                    JNIEnv* env, jclass cls,
+                                    jstring text, jint newCursorPosition)
+{
+    const char *utftext = (*env)->GetStringUTFChars(env, text, NULL);
+
+    SDL_SendEditingText(utftext, 0, 0);
+
+    (*env)->ReleaseStringUTFChars(env, text, utftext);
+}
+
+
+
+/*******************************************************************************
+             Functions called by SDL into Java
+*******************************************************************************/
+
+static int s_active = 0;
+struct LocalReferenceHolder
+{
+    JNIEnv *m_env;
+    const char *m_func;
+};
+
+static struct LocalReferenceHolder LocalReferenceHolder_Setup(const char *func)
+{
+    struct LocalReferenceHolder refholder;
+    refholder.m_env = NULL;
+    refholder.m_func = func;
+#ifdef DEBUG_JNI
+    SDL_Log("Entering function %s", func);
+#endif
+    return refholder;
+}
+
+static SDL_bool LocalReferenceHolder_Init(struct LocalReferenceHolder *refholder, JNIEnv *env)
+{
+    const int capacity = 16;
+    if ((*env)->PushLocalFrame(env, capacity) < 0) {
+        SDL_SetError("Failed to allocate enough JVM local references");
+        return false;
+    }
+    ++s_active;
+    refholder->m_env = env;
+    return SDL_TRUE;
+}
+
+static void LocalReferenceHolder_Cleanup(struct LocalReferenceHolder *refholder)
+{
+#ifdef DEBUG_JNI
+    SDL_Log("Leaving function %s", refholder->m_func);
+#endif
+    if (refholder->m_env) {
+        JNIEnv* env = refholder->m_env;
+        (*env)->PopLocalFrame(env, NULL);
+        --s_active;
+    }
+}
+
+static SDL_bool LocalReferenceHolder_IsActive()
+{
+    return s_active > 0;    
+}
+
+
+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)
+{
+    JNIEnv *env = Android_JNI_GetEnv();
+
+    jint attribs[] = {
+        EGL_RED_SIZE, red,
+        EGL_GREEN_SIZE, green,
+        EGL_BLUE_SIZE, blue,
+        EGL_ALPHA_SIZE, alpha,
+        EGL_BUFFER_SIZE, buffer,
+        EGL_DEPTH_SIZE, depth,
+        EGL_STENCIL_SIZE, stencil,
+        EGL_SAMPLE_BUFFERS, buffers,
+        EGL_SAMPLES, samples,
+        EGL_RENDERABLE_TYPE, (majorVersion == 1 ? EGL_OPENGL_ES_BIT : EGL_OPENGL_ES2_BIT),
+        EGL_NONE
+    };
+    int len = SDL_arraysize(attribs);
+
+    jintArray array;
+
+    array = (*env)->NewIntArray(env, len);
+    (*env)->SetIntArrayRegion(env, array, 0, len, attribs);
+
+    jboolean success = (*env)->CallStaticBooleanMethod(env, mActivityClass, midCreateGLContext, majorVersion, minorVersion, array);
+
+    (*env)->DeleteLocalRef(env, array);
+
+    return success ? SDL_TRUE : SDL_FALSE;
+}
+
+void Android_JNI_SwapWindow()
+{
+    JNIEnv *mEnv = Android_JNI_GetEnv();
+    (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midFlipBuffers);
+}
+
+void Android_JNI_SetActivityTitle(const char *title)
+{
+    jmethodID mid;
+    JNIEnv *mEnv = Android_JNI_GetEnv();
+    mid = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,"setActivityTitle","(Ljava/lang/String;)Z");
+    if (mid) {
+        jstring jtitle = (jstring)((*mEnv)->NewStringUTF(mEnv, title));
+        (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, mid, jtitle);
+        (*mEnv)->DeleteLocalRef(mEnv, jtitle);
+    }
+}
+
+SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
+{
+    int i;
+    SDL_bool retval = SDL_FALSE;
+
+    if (bHasNewData) {
+        for (i = 0; i < 3; ++i) {
+            values[i] = fLastAccelerometer[i];
+        }
+        bHasNewData = false;
+        retval = SDL_TRUE;
+    }
+
+    return retval;
+}
+
+static void Android_JNI_ThreadDestroyed(void* value) {
+    /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
+    JNIEnv *env = (JNIEnv*) value;
+    if (env != NULL) {
+        (*mJavaVM)->DetachCurrentThread(mJavaVM);
+        pthread_setspecific(mThreadKey, NULL);
+    }
+}
+
+JNIEnv* Android_JNI_GetEnv(void) {
+    /* From http://developer.android.com/guide/practices/jni.html
+     * All threads are Linux threads, scheduled by the kernel.
+     * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then
+     * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the
+     * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv,
+     * and cannot make JNI calls.
+     * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main"
+     * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread
+     * is a no-op.
+     * Note: You can call this function any number of times for the same thread, there's no harm in it
+     */
+
+    JNIEnv *env;
+    int status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);
+    if(status < 0) {
+        LOGE("failed to attach current thread");
+        return 0;
+    }
+
+    return env;
+}
+
+int Android_JNI_SetupThread(void) {
+    /* From http://developer.android.com/guide/practices/jni.html
+     * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward,
+     * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be
+     * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific
+     * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.)
+     * Note: The destructor is not called unless the stored value is != NULL
+     * Note: You can call this function any number of times for the same thread, there's no harm in it
+     *       (except for some lost CPU cycles)
+     */
+    JNIEnv *env = Android_JNI_GetEnv();
+    pthread_setspecific(mThreadKey, (void*) env);
+    return 1;
+}
+
+//
+// Audio support
+//
+static jboolean audioBuffer16Bit = JNI_FALSE;
+static jboolean audioBufferStereo = JNI_FALSE;
+static jobject audioBuffer = NULL;
+static void* audioBufferPinned = NULL;
+
+int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
+{
+    int audioBufferFrames;
+
+    JNIEnv *env = Android_JNI_GetEnv();
+
+    if (!env) {
+        LOGE("callback_handler: failed to attach current thread");
+    }
+    Android_JNI_SetupThread();
+
+    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
+    audioBuffer16Bit = is16Bit;
+    audioBufferStereo = channelCount > 1;
+
+    (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames);
+
+    /* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on
+     * Android >= 4.2 due to a "stale global reference" error. So now we allocate this buffer directly from this side. */
+
+    if (is16Bit) {
+        jshortArray audioBufferLocal = (*env)->NewShortArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
+        if (audioBufferLocal) {
+            audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
+            (*env)->DeleteLocalRef(env, audioBufferLocal);
+        }
+    }
+    else {
+        jbyteArray audioBufferLocal = (*env)->NewByteArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
+        if (audioBufferLocal) {
+            audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
+            (*env)->DeleteLocalRef(env, audioBufferLocal);
+        }
+    }
+
+    if (audioBuffer == NULL) {
+        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer!");
+        return 0;
+    }
+
+    jboolean isCopy = JNI_FALSE;
+    if (audioBuffer16Bit) {
+        audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
+        audioBufferFrames = (*env)->GetArrayLength(env, (jshortArray)audioBuffer);
+    } else {
+        audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
+        audioBufferFrames = (*env)->GetArrayLength(env, (jbyteArray)audioBuffer);
+    }
+    if (audioBufferStereo) {
+        audioBufferFrames /= 2;
+    }
+
+    return audioBufferFrames;
+}
+
+void * Android_JNI_GetAudioBuffer()
+{
+    return audioBufferPinned;
+}
+
+void Android_JNI_WriteAudioBuffer()
+{
+    JNIEnv *mAudioEnv = Android_JNI_GetEnv();
+
+    if (audioBuffer16Bit) {
+        (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
+        (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
+    } else {
+        (*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
+        (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
+    }
+
+    /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
+}
+
+void Android_JNI_CloseAudioDevice()
+{
+    JNIEnv *env = Android_JNI_GetEnv();
+
+    (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioQuit);
+
+    if (audioBuffer) {
+        (*env)->DeleteGlobalRef(env, audioBuffer);
+        audioBuffer = NULL;
+        audioBufferPinned = NULL;
+    }
+}
+
+// Test for an exception and call SDL_SetError with its detail if one occurs
+// If optional parameter silent is truthy then SDL_SetError() is not called.
+static bool Android_JNI_ExceptionOccurred(bool silent)
+{
+    SDL_assert(LocalReferenceHolder_IsActive());
+    JNIEnv *mEnv = Android_JNI_GetEnv();
+
+    jthrowable exception = (*mEnv)->ExceptionOccurred(mEnv);
+    if (exception != NULL) {
+        jmethodID mid;
+
+        // Until this happens most JNI operations have undefined behaviour
+        (*mEnv)->ExceptionClear(mEnv);
+
+        if (!silent) {
+            jclass exceptionClass = (*mEnv)->GetObjectClass(mEnv, exception);
+            jclass classClass = (*mEnv)->FindClass(mEnv, "java/lang/Class");
+
+            mid = (*mEnv)->GetMethodID(mEnv, classClass, "getName", "()Ljava/lang/String;");
+            jstring exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid);
+            const char* exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0);
+
+            mid = (*mEnv)->GetMethodID(mEnv, exceptionClass, "getMessage", "()Ljava/lang/String;");
+            jstring exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid);
+
+            if (exceptionMessage != NULL) {
+                const char* exceptionMessageUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionMessage, 0);
+                SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
+                (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionMessage, exceptionMessageUTF8);
+            } else {
+                SDL_SetError("%s", exceptionNameUTF8);
+            }
+
+            (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionName, exceptionNameUTF8);
+        }
+
+        return true;
+    }
+
+    return false;
+}
+
+static int Internal_Android_JNI_FileOpen(SDL_RWops* ctx)
+{
+    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
+
+    int result = 0;
+
+    jmethodID mid;
+    jobject context;
+    jobject assetManager;
+    jobject inputStream;
+    jclass channels;
+    jobject readableByteChannel;
+    jstring fileNameJString;
+    jobject fd;
+    jclass fdCls;
+    jfieldID descriptor;
+
+    JNIEnv *mEnv = Android_JNI_GetEnv();
+    if (!LocalReferenceHolder_Init(&refs, mEnv)) {
+        goto failure;
+    }
+
+    fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef;
+    ctx->hidden.androidio.position = 0;
+
+    // context = SDLActivity.getContext();
+    mid = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+            "getContext","()Landroid/content/Context;");
+    context = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, mid);
+
+
+    // assetManager = context.getAssets();
+    mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
+            "getAssets", "()Landroid/content/res/AssetManager;");
+    assetManager = (*mEnv)->CallObjectMethod(mEnv, context, mid);
+
+    /* First let's try opening the file to obtain an AssetFileDescriptor.
+    * This method reads the files directly from the APKs using standard *nix calls
+    */
+    mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
+    inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString);
+    if (Android_JNI_ExceptionOccurred(true)) {
+        goto fallback;
+    }
+
+    mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getStartOffset", "()J");
+    ctx->hidden.androidio.offset = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
+    if (Android_JNI_ExceptionOccurred(true)) {
+        goto fallback;
+    }
+
+    mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getDeclaredLength", "()J");
+    ctx->hidden.androidio.size = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
+    if (Android_JNI_ExceptionOccurred(true)) {
+        goto fallback;
+    }
+
+    mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;");
+    fd = (*mEnv)->CallObjectMethod(mEnv, inputStream, mid);
+    fdCls = (*mEnv)->GetObjectClass(mEnv, fd);
+    descriptor = (*mEnv)->GetFieldID(mEnv, fdCls, "descriptor", "I");
+    ctx->hidden.androidio.fd = (*mEnv)->GetIntField(mEnv, fd, descriptor);
+    ctx->hidden.androidio.assetFileDescriptorRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
+
+    // Seek to the correct offset in the file.
+    lseek(ctx->hidden.androidio.fd, (off_t)ctx->hidden.androidio.offset, SEEK_SET);
+
+    if (false) {
+fallback:
+        // Disabled log message because of spam on the Nexus 7
+        //__android_log_print(ANDROID_LOG_DEBUG, "SDL", "Falling back to legacy InputStream method for opening file");
+
+        /* Try the old method using InputStream */
+        ctx->hidden.androidio.assetFileDescriptorRef = NULL;
+
+        // inputStream = assetManager.open(<filename>);
+        mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager),
+                "open", "(Ljava/lang/String;I)Ljava/io/InputStream;");
+        inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 /*ACCESS_RANDOM*/);
+        if (Android_JNI_ExceptionOccurred(false)) {
+            goto failure;
+        }
+
+        ctx->hidden.androidio.inputStreamRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
+
+        // Despite all the visible documentation on [Asset]InputStream claiming
+        // that the .available() method is not guaranteed to return the entire file
+        // size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ...
+        // android/apis/content/ReadAsset.java imply that Android's
+        // AssetInputStream.available() /will/ always return the total file size
+
+        // size = inputStream.available();
+        mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
+                "available", "()I");
+        ctx->hidden.androidio.size = (long)(*mEnv)->CallIntMethod(mEnv, inputStream, mid);
+        if (Android_JNI_ExceptionOccurred(false)) {
+            goto failure;
+        }
+
+        // readableByteChannel = Channels.newChannel(inputStream);
+        channels = (*mEnv)->FindClass(mEnv, "java/nio/channels/Channels");
+        mid = (*mEnv)->GetStaticMethodID(mEnv, channels,
+                "newChannel",
+                "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
+        readableByteChannel = (*mEnv)->CallStaticObjectMethod(
+                mEnv, channels, mid, inputStream);
+        if (Android_JNI_ExceptionOccurred(false)) {
+            goto failure;
+        }
+
+        ctx->hidden.androidio.readableByteChannelRef =
+            (*mEnv)->NewGlobalRef(mEnv, readableByteChannel);
+
+        // Store .read id for reading purposes
+        mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, readableByteChannel),
+                "read", "(Ljava/nio/ByteBuffer;)I");
+        ctx->hidden.androidio.readMethod = mid;
+    }
+
+    if (false) {
+failure:
+        result = -1;
+
+        (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef);
+
+        if(ctx->hidden.androidio.inputStreamRef != NULL) {
+            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef);
+        }
+
+        if(ctx->hidden.androidio.readableByteChannelRef != NULL) {
+            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef);
+        }
+
+        if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) {
+            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
+        }
+
+    }
+    
+    LocalReferenceHolder_Cleanup(&refs);
+    return result;
+}
+
+int Android_JNI_FileOpen(SDL_RWops* ctx,
+        const char* fileName, const char* mode)
+{
+    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
+    JNIEnv *mEnv = Android_JNI_GetEnv();
+    int retval;
+
+    if (!LocalReferenceHolder_Init(&refs, mEnv)) {
+        LocalReferenceHolder_Cleanup(&refs);        
+        return -1;
+    }
+
+    if (!ctx) {
+        LocalReferenceHolder_Cleanup(&refs);
+        return -1;
+    }
+
+    jstring fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName);
+    ctx->hidden.androidio.fileNameRef = (*mEnv)->NewGlobalRef(mEnv, fileNameJString);
+    ctx->hidden.androidio.inputStreamRef = NULL;
+    ctx->hidden.androidio.readableByteChannelRef = NULL;
+    ctx->hidden.androidio.readMethod = NULL;
+    ctx->hidden.androidio.assetFileDescriptorRef = NULL;
+
+    retval = Internal_Android_JNI_FileOpen(ctx);
+    LocalReferenceHolder_Cleanup(&refs);
+    return retval;
+}
+
+size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
+        size_t size, size_t maxnum)
+{
+    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
+
+    if (ctx->hidden.androidio.assetFileDescriptorRef) {
+        size_t bytesMax = size * maxnum;
+        if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) {
+            bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position;
+        }
+        size_t result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
+        if (result > 0) {
+            ctx->hidden.androidio.position += result;
+            LocalReferenceHolder_Cleanup(&refs);
+            return result / size;
+        }
+        LocalReferenceHolder_Cleanup(&refs);
+        return 0;
+    } else {
+        jlong bytesRemaining = (jlong) (size * maxnum);
+        jlong bytesMax = (jlong) (ctx->hidden.androidio.size -  ctx->hidden.androidio.position);
+        int bytesRead = 0;
+
+        /* Don't read more bytes than those that remain in the file, otherwise we get an exception */
+        if (bytesRemaining >  bytesMax) bytesRemaining = bytesMax;
+
+        JNIEnv *mEnv = Android_JNI_GetEnv();
+        if (!LocalReferenceHolder_Init(&refs, mEnv)) {
+            LocalReferenceHolder_Cleanup(&refs);            
+            return 0;
+        }
+
+        jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
+        jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
+        jobject byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv, buffer, bytesRemaining);
+
+        while (bytesRemaining > 0) {
+            // result = readableByteChannel.read(...);
+            int result = (*mEnv)->CallIntMethod(mEnv, readableByteChannel, readMethod, byteBuffer);
+
+            if (Android_JNI_ExceptionOccurred(false)) {
+                LocalReferenceHolder_Cleanup(&refs);            
+                return 0;
+            }
+
+            if (result < 0) {
+                break;
+            }
+
+            bytesRemaining -= result;
+            bytesRead += result;
+            ctx->hidden.androidio.position += result;
+        }
+        LocalReferenceHolder_Cleanup(&refs);                    
+        return bytesRead / size;
+    }
+    LocalReferenceHolder_Cleanup(&refs);            
+}
+
+size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer,
+        size_t size, size_t num)
+{
+    SDL_SetError("Cannot write to Android package filesystem");
+    return 0;
+}
+
+static int Internal_Android_JNI_FileClose(SDL_RWops* ctx, bool release)
+{
+    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
+
+    int result = 0;
+    JNIEnv *mEnv = Android_JNI_GetEnv();
+
+    if (!LocalReferenceHolder_Init(&refs, mEnv)) {
+        LocalReferenceHolder_Cleanup(&refs);
+        return SDL_SetError("Failed to allocate enough JVM local references");
+    }
+
+    if (ctx) {
+        if (release) {
+            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef);
+        }
+
+        if (ctx->hidden.androidio.assetFileDescriptorRef) {
+            jobject inputStream = (jobject)ctx->hidden.androidio.assetFileDescriptorRef;
+            jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
+                    "close", "()V");
+            (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
+            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
+            if (Android_JNI_ExceptionOccurred(false)) {
+                result = -1;
+            }
+        }
+        else {
+            jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef;
+
+            // inputStream.close();
+            jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
+                    "close", "()V");
+            (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
+            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef);
+            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef);
+            if (Android_JNI_ExceptionOccurred(false)) {
+                result = -1;
+            }
+        }
+
+        if (release) {
+            SDL_FreeRW(ctx);
+        }
+    }
+
+    LocalReferenceHolder_Cleanup(&refs);
+    return result;
+}
+
+
+Sint64 Android_JNI_FileSize(SDL_RWops* ctx)
+{
+    return ctx->hidden.androidio.size;
+}
+
+Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
+{
+    if (ctx->hidden.androidio.assetFileDescriptorRef) {
+        switch (whence) {
+            case RW_SEEK_SET:
+                if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
+                offset += ctx->hidden.androidio.offset;
+                break;
+            case RW_SEEK_CUR:
+                offset += ctx->hidden.androidio.position;
+                if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
+                offset += ctx->hidden.androidio.offset;
+                break;
+            case RW_SEEK_END:
+                offset = ctx->hidden.androidio.offset + ctx->hidden.androidio.size + offset;
+                break;
+            default:
+                return SDL_SetError("Unknown value for 'whence'");
+        }
+        whence = SEEK_SET;
+
+        off_t ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
+        if (ret == -1) return -1;
+        ctx->hidden.androidio.position = ret - ctx->hidden.androidio.offset;
+    } else {
+        Sint64 newPosition;
+
+        switch (whence) {
+            case RW_SEEK_SET:
+                newPosition = offset;
+                break;
+            case RW_SEEK_CUR:
+                newPosition = ctx->hidden.androidio.position + offset;
+                break;
+            case RW_SEEK_END:
+                newPosition = ctx->hidden.androidio.size + offset;
+                break;
+            default:
+                return SDL_SetError("Unknown value for 'whence'");
+        }
+
+        /* Validate the new position */
+        if (newPosition < 0) {
+            return SDL_Error(SDL_EFSEEK);
+        }
+        if (newPosition > ctx->hidden.androidio.size) {
+            newPosition = ctx->hidden.androidio.size;
+        }
+
+        Sint64 movement = newPosition - ctx->hidden.androidio.position;
+        if (movement > 0) {
+            unsigned char buffer[4096];
+
+            // The easy case where we're seeking forwards
+            while (movement > 0) {
+                Sint64 amount = sizeof (buffer);
+                if (amount > movement) {
+                    amount = movement;
+                }
+                size_t result = Android_JNI_FileRead(ctx, buffer, 1, amount);
+                if (result <= 0) {
+                    // Failed to read/skip the required amount, so fail
+                    return -1;
+                }
+
+                movement -= result;
+            }
+
+        } else if (movement < 0) {
+            // We can't seek backwards so we have to reopen the file and seek
+            // forwards which obviously isn't very efficient
+            Internal_Android_JNI_FileClose(ctx, false);
+            Internal_Android_JNI_FileOpen(ctx);
+            Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
+        }
+    }
+
+    return ctx->hidden.androidio.position;
+
+}
+
+int Android_JNI_FileClose(SDL_RWops* ctx)
+{
+    return Internal_Android_JNI_FileClose(ctx, true);
+}
+
+// returns a new global reference which needs to be released later
+static jobject Android_JNI_GetSystemServiceObject(const char* name)
+{
+    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
+    JNIEnv* env = Android_JNI_GetEnv();
+    jobject retval = NULL;
+
+    if (!LocalReferenceHolder_Init(&refs, env)) {
+        LocalReferenceHolder_Cleanup(&refs);
+        return NULL;
+    }
+
+    jstring service = (*env)->NewStringUTF(env, name);
+
+    jmethodID mid;
+
+    mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;");
+    jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
+
+    mid = (*env)->GetMethodID(env, mActivityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
+    jobject manager = (*env)->CallObjectMethod(env, context, mid, service);
+
+    (*env)->DeleteLocalRef(env, service);
+
+    retval = manager ? (*env)->NewGlobalRef(env, manager) : NULL;
+    LocalReferenceHolder_Cleanup(&refs);
+    return retval;
+}
+
+#define SETUP_CLIPBOARD(error) \
+    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); \
+    JNIEnv* env = Android_JNI_GetEnv(); \
+    if (!LocalReferenceHolder_Init(&refs, env)) { \
+        LocalReferenceHolder_Cleanup(&refs); \
+        return error; \
+    } \
+    jobject clipboard = Android_JNI_GetSystemServiceObject("clipboard"); \
+    if (!clipboard) { \
+        LocalReferenceHolder_Cleanup(&refs); \
+        return error; \
+    }
+
+#define CLEANUP_CLIPBOARD() \
+    LocalReferenceHolder_Cleanup(&refs);
+
+int Android_JNI_SetClipboardText(const char* text)
+{
+    SETUP_CLIPBOARD(-1)
+
+    jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "setText", "(Ljava/lang/CharSequence;)V");
+    jstring string = (*env)->NewStringUTF(env, text);
+    (*env)->CallVoidMethod(env, clipboard, mid, string);
+    (*env)->DeleteGlobalRef(env, clipboard);
+    (*env)->DeleteLocalRef(env, string);
+
+    CLEANUP_CLIPBOARD();
+
+    return 0;
+}
+
+char* Android_JNI_GetClipboardText()
+{
+    SETUP_CLIPBOARD(SDL_strdup(""))
+
+    jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "getText", "()Ljava/lang/CharSequence;");
+    jobject sequence = (*env)->CallObjectMethod(env, clipboard, mid);
+    (*env)->DeleteGlobalRef(env, clipboard);
+    if (sequence) {
+        mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, sequence), "toString", "()Ljava/lang/String;");
+        jstring string = (jstring)((*env)->CallObjectMethod(env, sequence, mid));
+        const char* utf = (*env)->GetStringUTFChars(env, string, 0);
+        if (utf) {
+            char* text = SDL_strdup(utf);
+            (*env)->ReleaseStringUTFChars(env, string, utf);
+
+            CLEANUP_CLIPBOARD();
+
+            return text;
+        }
+    }
+
+    CLEANUP_CLIPBOARD();    
+
+    return SDL_strdup("");
+}
+
+SDL_bool Android_JNI_HasClipboardText()
+{
+    SETUP_CLIPBOARD(SDL_FALSE)
+
+    jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "hasText", "()Z");
+    jboolean has = (*env)->CallBooleanMethod(env, clipboard, mid);
+    (*env)->DeleteGlobalRef(env, clipboard);
+
+    CLEANUP_CLIPBOARD();
+    
+    return has ? SDL_TRUE : SDL_FALSE;
+}
+
+
+// returns 0 on success or -1 on error (others undefined then)
+// returns truthy or falsy value in plugged, charged and battery
+// returns the value in seconds and percent or -1 if not available
+int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent)
+{
+    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
+    JNIEnv* env = Android_JNI_GetEnv();
+    if (!LocalReferenceHolder_Init(&refs, env)) {
+        LocalReferenceHolder_Cleanup(&refs);
+        return -1;
+    }
+
+    jmethodID mid;
+
+    mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;");
+    jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
+
+    jstring action = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED");
+
+    jclass cls = (*env)->FindClass(env, "android/content/IntentFilter");
+
+    mid = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
+    jobject filter = (*env)->NewObject(env, cls, mid, action);
+
+    (*env)->DeleteLocalRef(env, action);
+
+    mid = (*env)->GetMethodID(env, mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
+    jobject intent = (*env)->CallObjectMethod(env, context, mid, NULL, filter);
+
+    (*env)->DeleteLocalRef(env, filter);
+
+    cls = (*env)->GetObjectClass(env, intent);
+
+    jstring iname;
+    jmethodID imid = (*env)->GetMethodID(env, cls, "getIntExtra", "(Ljava/lang/String;I)I");
+
+#define GET_INT_EXTRA(var, key) \
+    iname = (*env)->NewStringUTF(env, key); \
+    int var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \
+    (*env)->DeleteLocalRef(env, iname);
+
+    jstring bname;
+    jmethodID bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
+
+#define GET_BOOL_EXTRA(var, key) \
+    bname = (*env)->NewStringUTF(env, key); \
+    int var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \
+    (*env)->DeleteLocalRef(env, bname);
+
+    if (plugged) {
+        GET_INT_EXTRA(plug, "plugged") // == BatteryManager.EXTRA_PLUGGED (API 5)
+        if (plug == -1) {
+            LocalReferenceHolder_Cleanup(&refs);
+            return -1;
+        }
+        // 1 == BatteryManager.BATTERY_PLUGGED_AC
+        // 2 == BatteryManager.BATTERY_PLUGGED_USB
+        *plugged = (0 < plug) ? 1 : 0;
+    }
+
+    if (charged) {
+        GET_INT_EXTRA(status, "status") // == BatteryManager.EXTRA_STATUS (API 5)
+        if (status == -1) {
+            LocalReferenceHolder_Cleanup(&refs);
+            return -1;
+        }
+        // 5 == BatteryManager.BATTERY_STATUS_FULL
+        *charged = (status == 5) ? 1 : 0;
+    }
+
+    if (battery) {
+        GET_BOOL_EXTRA(present, "present") // == BatteryManager.EXTRA_PRESENT (API 5)
+        *battery = present ? 1 : 0;
+    }
+
+    if (seconds) {
+        *seconds = -1; // not possible
+    }
+
+    if (percent) {
+        GET_INT_EXTRA(level, "level") // == BatteryManager.EXTRA_LEVEL (API 5)
+        GET_INT_EXTRA(scale, "scale") // == BatteryManager.EXTRA_SCALE (API 5)
+        if ((level == -1) || (scale == -1)) {
+            LocalReferenceHolder_Cleanup(&refs);
+            return -1;
+        }
+        *percent = level * 100 / scale;
+    }
+
+    (*env)->DeleteLocalRef(env, intent);
+
+    LocalReferenceHolder_Cleanup(&refs);
+    return 0;
+}
+
+// sends message to be handled on the UI event dispatch thread
+int Android_JNI_SendMessage(int command, int param)
+{
+    JNIEnv *env = Android_JNI_GetEnv();
+    if (!env) {
+        return -1;
+    }
+    jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z");
+    if (!mid) {
+        return -1;
+    }
+    jboolean success = (*env)->CallStaticBooleanMethod(env, mActivityClass, mid, command, param);
+    return success ? 0 : -1;
+}
+
+void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
+{
+    JNIEnv *env = Android_JNI_GetEnv();
+    if (!env) {
+        return;
+    }
+
+    jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIII)Z");
+    if (!mid) {
+        return;
+    }
+    (*env)->CallStaticBooleanMethod(env, mActivityClass, mid,
+                               inputRect->x,
+                               inputRect->y,
+                               inputRect->w,
+                               inputRect->h );
+}
+
+void Android_JNI_HideTextInput()
+{
+    // has to match Activity constant
+    const int COMMAND_TEXTEDIT_HIDE = 3;
+    Android_JNI_SendMessage(COMMAND_TEXTEDIT_HIDE, 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Functions exposed to SDL applications in SDL_system.h
+//
+
+void *SDL_AndroidGetJNIEnv()
+{
+    return Android_JNI_GetEnv();
+}
+
+
+
+void *SDL_AndroidGetActivity()
+{
+    /* See SDL_system.h for caveats on using this function. */
+
+    jmethodID mid;
+
+    JNIEnv *env = Android_JNI_GetEnv();
+    if (!env) {
+        return NULL;
+    }
+
+    // return SDLActivity.getContext();
+    mid = (*env)->GetStaticMethodID(env, mActivityClass,
+            "getContext","()Landroid/content/Context;");
+    return (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
+}
+
+const char * SDL_AndroidGetInternalStoragePath()
+{
+    static char *s_AndroidInternalFilesPath = NULL;
+
+    if (!s_AndroidInternalFilesPath) {
+        struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
+        jmethodID mid;
+        jobject context;
+        jobject fileObject;
+        jstring pathString;
+        const char *path;
+
+        JNIEnv *env = Android_JNI_GetEnv();
+        if (!LocalReferenceHolder_Init(&refs, env)) {
+            LocalReferenceHolder_Cleanup(&refs);
+            return NULL;
+        }
+
+        // context = SDLActivity.getContext();
+        mid = (*env)->GetStaticMethodID(env, mActivityClass,
+                "getContext","()Landroid/content/Context;");
+        context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
+
+        // fileObj = context.getFilesDir();
+        mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
+                "getFilesDir", "()Ljava/io/File;");
+        fileObject = (*env)->CallObjectMethod(env, context, mid);
+        if (!fileObject) {
+            SDL_SetError("Couldn't get internal directory");
+            LocalReferenceHolder_Cleanup(&refs);
+            return NULL;
+        }
+
+        // path = fileObject.getAbsolutePath();
+        mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
+                "getAbsolutePath", "()Ljava/lang/String;");
+        pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
+
+        path = (*env)->GetStringUTFChars(env, pathString, NULL);
+        s_AndroidInternalFilesPath = SDL_strdup(path);
+        (*env)->ReleaseStringUTFChars(env, pathString, path);
+
+        LocalReferenceHolder_Cleanup(&refs);
+    }
+    return s_AndroidInternalFilesPath;
+}
+
+int SDL_AndroidGetExternalStorageState()
+{
+    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
+    jmethodID mid;
+    jclass cls;
+    jstring stateString;
+    const char *state;
+    int stateFlags;
+
+    JNIEnv *env = Android_JNI_GetEnv();
+    if (!LocalReferenceHolder_Init(&refs, env)) {
+        LocalReferenceHolder_Cleanup(&refs);
+        return 0;
+    }
+
+    cls = (*env)->FindClass(env, "android/os/Environment");
+    mid = (*env)->GetStaticMethodID(env, cls,
+            "getExternalStorageState", "()Ljava/lang/String;");
+    stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid);
+
+    state = (*env)->GetStringUTFChars(env, stateString, NULL);
+
+    // Print an info message so people debugging know the storage state
+    __android_log_print(ANDROID_LOG_INFO, "SDL", "external storage state: %s", state);
+
+    if (SDL_strcmp(state, "mounted") == 0) {
+        stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ |
+                     SDL_ANDROID_EXTERNAL_STORAGE_WRITE;
+    } else if (SDL_strcmp(state, "mounted_ro") == 0) {
+        stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ;
+    } else {
+        stateFlags = 0;
+    }
+    (*env)->ReleaseStringUTFChars(env, stateString, state);
+
+    LocalReferenceHolder_Cleanup(&refs);
+    return stateFlags;
+}
+
+const char * SDL_AndroidGetExternalStoragePath()
+{
+    static char *s_AndroidExternalFilesPath = NULL;
+
+    if (!s_AndroidExternalFilesPath) {
+        struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
+        jmethodID mid;
+        jobject context;
+        jobject fileObject;
+        jstring pathString;
+        const char *path;
+
+        JNIEnv *env = Android_JNI_GetEnv();
+        if (!LocalReferenceHolder_Init(&refs, env)) {
+            LocalReferenceHolder_Cleanup(&refs);
+            return NULL;
+        }
+
+        // context = SDLActivity.getContext();
+        mid = (*env)->GetStaticMethodID(env, mActivityClass,
+                "getContext","()Landroid/content/Context;");
+        context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
+
+        // fileObj = context.getExternalFilesDir();
+        mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
+                "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
+        fileObject = (*env)->CallObjectMethod(env, context, mid, NULL);
+        if (!fileObject) {
+            SDL_SetError("Couldn't get external directory");
+            LocalReferenceHolder_Cleanup(&refs);
+            return NULL;
+        }
+
+        // path = fileObject.getAbsolutePath();
+        mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
+                "getAbsolutePath", "()Ljava/lang/String;");
+        pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
+
+        path = (*env)->GetStringUTFChars(env, pathString, NULL);
+        s_AndroidExternalFilesPath = SDL_strdup(path);
+        (*env)->ReleaseStringUTFChars(env, pathString, path);
+
+        LocalReferenceHolder_Cleanup(&refs);
+    }
+    return s_AndroidExternalFilesPath;
+}
+
+#endif /* __ANDROID__ */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- a/src/core/android/SDL_android.cpp	Mon Jul 22 22:54:00 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1295 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
-
-  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"
-#include "SDL_stdinc.h"
-#include "SDL_assert.h"
-#include "SDL_log.h"
-
-#ifdef __ANDROID__
-
-#include "SDL_system.h"
-#include "SDL_android.h"
-#include <EGL/egl.h>
-
-extern "C" {
-#include "../../events/SDL_events_c.h"
-#include "../../video/android/SDL_androidkeyboard.h"
-#include "../../video/android/SDL_androidtouch.h"
-#include "../../video/android/SDL_androidvideo.h"
-
-#include <android/log.h>
-#include <pthread.h>
-#include <sys/types.h>
-#include <unistd.h>
-#define LOG_TAG "SDL_android"
-//#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
-//#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
-#define LOGI(...) do {} while (false)
-#define LOGE(...) do {} while (false)
-
-/* Uncomment this to log messages entering and exiting methods in this file */
-//#define DEBUG_JNI
-
-/* Implemented in audio/android/SDL_androidaudio.c */
-extern void Android_RunAudioThread();
-
-static void Android_JNI_ThreadDestroyed(void*);
-} // C
-
-/*******************************************************************************
- This file links the Java side of Android with libsdl
-*******************************************************************************/
-#include <jni.h>
-#include <android/log.h>
-
-
-/*******************************************************************************
-                               Globals
-*******************************************************************************/
-static pthread_key_t mThreadKey;
-static JavaVM* mJavaVM;
-
-// Main activity
-static jclass mActivityClass;
-
-// method signatures
-static jmethodID midCreateGLContext;
-static jmethodID midFlipBuffers;
-static jmethodID midAudioInit;
-static jmethodID midAudioWriteShortBuffer;
-static jmethodID midAudioWriteByteBuffer;
-static jmethodID midAudioQuit;
-
-// Accelerometer data storage
-static float fLastAccelerometer[3];
-static bool bHasNewData;
-
-/*******************************************************************************
-                 Functions called by JNI
-*******************************************************************************/
-
-// Library init
-extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
-{
-    JNIEnv *env;
-    mJavaVM = vm;
-    LOGI("JNI_OnLoad called");
-    if (mJavaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
-        LOGE("Failed to get the environment using GetEnv()");
-        return -1;
-    }
-    /*
-     * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
-     * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
-     */
-    if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed)) {
-        __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing pthread key");
-    }
-    else {
-        Android_JNI_SetupThread();
-    }
-
-    return JNI_VERSION_1_4;
-}
-
-// Called before SDL_main() to initialize JNI bindings
-extern "C" void SDL_Android_Init(JNIEnv* mEnv, jclass cls)
-{
-    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init()");
-
-    Android_JNI_SetupThread();
-
-    mActivityClass = (jclass)mEnv->NewGlobalRef(cls);
-
-    midCreateGLContext = mEnv->GetStaticMethodID(mActivityClass,
-                                "createGLContext","(II[I)Z");
-    midFlipBuffers = mEnv->GetStaticMethodID(mActivityClass,
-                                "flipBuffers","()V");
-    midAudioInit = mEnv->GetStaticMethodID(mActivityClass,
-                                "audioInit", "(IZZI)V");
-    midAudioWriteShortBuffer = mEnv->GetStaticMethodID(mActivityClass,
-                                "audioWriteShortBuffer", "([S)V");
-    midAudioWriteByteBuffer = mEnv->GetStaticMethodID(mActivityClass,
-                                "audioWriteByteBuffer", "([B)V");
-    midAudioQuit = mEnv->GetStaticMethodID(mActivityClass,
-                                "audioQuit", "()V");
-
-    bHasNewData = false;
-
-    if(!midCreateGLContext || !midFlipBuffers || !midAudioInit ||
-       !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit) {
-        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
-    }
-    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init() finished!");
-}
-
-// Resize
-extern "C" void Java_org_libsdl_app_SDLActivity_onNativeResize(
-                                    JNIEnv* env, jclass jcls,
-                                    jint width, jint height, jint format)
-{
-    Android_SetScreenResolution(width, height, format);
-}
-
-// Keydown
-extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyDown(
-                                    JNIEnv* env, jclass jcls, jint keycode)
-{
-    Android_OnKeyDown(keycode);
-}
-
-// Keyup
-extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyUp(
-                                    JNIEnv* env, jclass jcls, jint keycode)
-{
-    Android_OnKeyUp(keycode);
-}
-
-// Touch
-extern "C" void Java_org_libsdl_app_SDLActivity_onNativeTouch(
-                                    JNIEnv* env, jclass jcls,
-                                    jint touch_device_id_in, jint pointer_finger_id_in,
-                                    jint action, jfloat x, jfloat y, jfloat p)
-{
-    Android_OnTouch(touch_device_id_in, pointer_finger_id_in, action, x, y, p);
-}
-
-// Accelerometer
-extern "C" void Java_org_libsdl_app_SDLActivity_onNativeAccel(
-                                    JNIEnv* env, jclass jcls,
-                                    jfloat x, jfloat y, jfloat z)
-{
-    fLastAccelerometer[0] = x;
-    fLastAccelerometer[1] = y;
-    fLastAccelerometer[2] = z;
-    bHasNewData = true;
-}
-
-// Low memory
-extern "C" void Java_org_libsdl_app_SDLActivity_nativeLowMemory(
-                                    JNIEnv* env, jclass cls)
-{
-    SDL_SendAppEvent(SDL_APP_LOWMEMORY);
-}
-
-// Quit
-extern "C" void Java_org_libsdl_app_SDLActivity_nativeQuit(
-                                    JNIEnv* env, jclass cls)
-{
-    // Inject a SDL_QUIT event
-    SDL_SendQuit();
-    SDL_SendAppEvent(SDL_APP_TERMINATING);
-}
-
-// Pause
-extern "C" void Java_org_libsdl_app_SDLActivity_nativePause(
-                                    JNIEnv* env, jclass cls)
-{
-    if (Android_Window) {
-        /* Signal the pause semaphore so the event loop knows to pause and (optionally) block itself */
-        if (!SDL_SemValue(Android_PauseSem)) SDL_SemPost(Android_PauseSem);
-        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
-        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
-    }
-
-    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
-    SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
-    SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
-}
-
-// Resume
-extern "C" void Java_org_libsdl_app_SDLActivity_nativeResume(
-                                    JNIEnv* env, jclass cls)
-{
-    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()");
-    SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
-    SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
-
-    if (Android_Window) {
-        /* Signal the resume semaphore so the event loop knows to resume and restore the GL Context
-         * We can't restore the GL Context here because it needs to be done on the SDL main thread
-         * and this function will be called from the Java thread instead.
-         */
-        if (!SDL_SemValue(Android_ResumeSem)) SDL_SemPost(Android_ResumeSem);
-        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
-        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0);
-    }
-}
-
-extern "C" void Java_org_libsdl_app_SDLActivity_nativeRunAudioThread(
-                                    JNIEnv* env, jclass cls)
-{
-    /* This is the audio thread, with a different environment */
-    Android_JNI_SetupThread();
-
-    Android_RunAudioThread();
-}
-
-extern "C" void Java_org_libsdl_app_SDLInputConnection_nativeCommitText(
-                                    JNIEnv* env, jclass cls,
-                                    jstring text, jint newCursorPosition)
-{
-    const char *utftext = env->GetStringUTFChars(text, NULL);
-
-    SDL_SendKeyboardText(utftext);
-
-    env->ReleaseStringUTFChars(text, utftext);
-}
-
-extern "C" void Java_org_libsdl_app_SDLInputConnection_nativeSetComposingText(
-                                    JNIEnv* env, jclass cls,
-                                    jstring text, jint newCursorPosition)
-{
-    const char *utftext = env->GetStringUTFChars(text, NULL);
-
-    SDL_SendEditingText(utftext, 0, 0);
-
-    env->ReleaseStringUTFChars(text, utftext);
-}
-
-
-
-/*******************************************************************************
-             Functions called by SDL into Java
-*******************************************************************************/
-
-class LocalReferenceHolder
-{
-private:
-    static int s_active;
-
-public:
-    static bool IsActive() {
-        return s_active > 0;
-    }
-
-public:
-    LocalReferenceHolder(const char *func) : m_env(NULL), m_func(func) {
-#ifdef DEBUG_JNI
-        SDL_Log("Entering function %s", m_func);
-#endif
-    }
-    ~LocalReferenceHolder() {
-#ifdef DEBUG_JNI
-        SDL_Log("Leaving function %s", m_func);
-#endif
-        if (m_env) {
-            m_env->PopLocalFrame(NULL);
-            --s_active;
-        }
-    }
-
-    bool init(JNIEnv *env, jint capacity = 16) {
-        if (env->PushLocalFrame(capacity) < 0) {
-            SDL_SetError("Failed to allocate enough JVM local references");
-            return false;
-        }
-        ++s_active;
-        m_env = env;
-        return true;
-    }
-
-protected:
-    JNIEnv *m_env;
-    const char *m_func;
-};
-int LocalReferenceHolder::s_active;
-
-extern "C" 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)
-{
-    JNIEnv *env = Android_JNI_GetEnv();
-
-    jint attribs[] = {
-        EGL_RED_SIZE, red,
-        EGL_GREEN_SIZE, green,
-        EGL_BLUE_SIZE, blue,
-        EGL_ALPHA_SIZE, alpha,
-        EGL_BUFFER_SIZE, buffer,
-        EGL_DEPTH_SIZE, depth,
-        EGL_STENCIL_SIZE, stencil,
-        EGL_SAMPLE_BUFFERS, buffers,
-        EGL_SAMPLES, samples,
-        EGL_RENDERABLE_TYPE, (majorVersion == 1 ? EGL_OPENGL_ES_BIT : EGL_OPENGL_ES2_BIT),
-        EGL_NONE
-    };
-    int len = SDL_arraysize(attribs);
-
-    jintArray array;
-
-    array = env->NewIntArray(len);
-    env->SetIntArrayRegion(array, 0, len, attribs);
-
-    jboolean success = env->CallStaticBooleanMethod(mActivityClass, midCreateGLContext, majorVersion, minorVersion, array);
-
-    env->DeleteLocalRef(array);
-
-    return success ? SDL_TRUE : SDL_FALSE;
-}
-
-extern "C" void Android_JNI_SwapWindow()
-{
-    JNIEnv *mEnv = Android_JNI_GetEnv();
-    mEnv->CallStaticVoidMethod(mActivityClass, midFlipBuffers);
-}
-
-extern "C" void Android_JNI_SetActivityTitle(const char *title)
-{
-    jmethodID mid;
-    JNIEnv *mEnv = Android_JNI_GetEnv();
-    mid = mEnv->GetStaticMethodID(mActivityClass,"setActivityTitle","(Ljava/lang/String;)Z");
-    if (mid) {
-        jstring jtitle = reinterpret_cast<jstring>(mEnv->NewStringUTF(title));
-        mEnv->CallStaticBooleanMethod(mActivityClass, mid, jtitle);
-        mEnv->DeleteLocalRef(jtitle);
-    }
-}
-
-extern "C" SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
-{
-    int i;
-    SDL_bool retval = SDL_FALSE;
-
-    if (bHasNewData) {
-        for (i = 0; i < 3; ++i) {
-            values[i] = fLastAccelerometer[i];
-        }
-        bHasNewData = false;
-        retval = SDL_TRUE;
-    }
-
-    return retval;
-}
-
-static void Android_JNI_ThreadDestroyed(void* value) {
-    /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
-    JNIEnv *env = (JNIEnv*) value;
-    if (env != NULL) {
-        mJavaVM->DetachCurrentThread();
-        pthread_setspecific(mThreadKey, NULL);
-    }
-}
-
-JNIEnv* Android_JNI_GetEnv(void) {
-    /* From http://developer.android.com/guide/practices/jni.html
-     * All threads are Linux threads, scheduled by the kernel.
-     * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then
-     * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the
-     * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv,
-     * and cannot make JNI calls.
-     * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main"
-     * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread
-     * is a no-op.
-     * Note: You can call this function any number of times for the same thread, there's no harm in it
-     */
-
-    JNIEnv *env;
-    int status = mJavaVM->AttachCurrentThread(&env, NULL);
-    if(status < 0) {
-        LOGE("failed to attach current thread");
-        return 0;
-    }
-
-    return env;
-}
-
-int Android_JNI_SetupThread(void) {
-    /* From http://developer.android.com/guide/practices/jni.html
-     * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward,
-     * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be
-     * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific
-     * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.)
-     * Note: The destructor is not called unless the stored value is != NULL
-     * Note: You can call this function any number of times for the same thread, there's no harm in it
-     *       (except for some lost CPU cycles)
-     */
-    JNIEnv *env = Android_JNI_GetEnv();
-    pthread_setspecific(mThreadKey, (void*) env);
-    return 1;
-}
-
-//
-// Audio support
-//
-static jboolean audioBuffer16Bit = JNI_FALSE;
-static jboolean audioBufferStereo = JNI_FALSE;
-static jobject audioBuffer = NULL;
-static void* audioBufferPinned = NULL;
-
-extern "C" int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
-{
-    int audioBufferFrames;
-
-    JNIEnv *env = Android_JNI_GetEnv();
-
-    if (!env) {
-        LOGE("callback_handler: failed to attach current thread");
-    }
-    Android_JNI_SetupThread();
-
-    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
-    audioBuffer16Bit = is16Bit;
-    audioBufferStereo = channelCount > 1;
-
-    env->CallStaticVoidMethod(mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames);
-
-    /* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on
-     * Android >= 4.2 due to a "stale global reference" error. So now we allocate this buffer directly from this side. */
-
-    if (is16Bit) {
-        jshortArray audioBufferLocal = env->NewShortArray(desiredBufferFrames * (audioBufferStereo ? 2 : 1));
-        if (audioBufferLocal) {
-            audioBuffer = env->NewGlobalRef(audioBufferLocal);
-            env->DeleteLocalRef(audioBufferLocal);
-        }
-    }
-    else {
-        jbyteArray audioBufferLocal = env->NewByteArray(desiredBufferFrames * (audioBufferStereo ? 2 : 1));
-        if (audioBufferLocal) {
-            audioBuffer = env->NewGlobalRef(audioBufferLocal);
-            env->DeleteLocalRef(audioBufferLocal);
-        }
-    }
-
-    if (audioBuffer == NULL) {
-        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer!");
-        return 0;
-    }
-
-    jboolean isCopy = JNI_FALSE;
-    if (audioBuffer16Bit) {
-        audioBufferPinned = env->GetShortArrayElements((jshortArray)audioBuffer, &isCopy);
-        audioBufferFrames = env->GetArrayLength((jshortArray)audioBuffer);
-    } else {
-        audioBufferPinned = env->GetByteArrayElements((jbyteArray)audioBuffer, &isCopy);
-        audioBufferFrames = env->GetArrayLength((jbyteArray)audioBuffer);
-    }
-    if (audioBufferStereo) {
-        audioBufferFrames /= 2;
-    }
-
-    return audioBufferFrames;
-}
-
-extern "C" void * Android_JNI_GetAudioBuffer()
-{
-    return audioBufferPinned;
-}
-
-extern "C" void Android_JNI_WriteAudioBuffer()
-{
-    JNIEnv *mAudioEnv = Android_JNI_GetEnv();
-
-    if (audioBuffer16Bit) {
-        mAudioEnv->ReleaseShortArrayElements((jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
-        mAudioEnv->CallStaticVoidMethod(mActivityClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
-    } else {
-        mAudioEnv->ReleaseByteArrayElements((jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
-        mAudioEnv->CallStaticVoidMethod(mActivityClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
-    }
-
-    /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
-}
-
-extern "C" void Android_JNI_CloseAudioDevice()
-{
-    JNIEnv *env = Android_JNI_GetEnv();
-
-    env->CallStaticVoidMethod(mActivityClass, midAudioQuit);
-
-    if (audioBuffer) {
-        env->DeleteGlobalRef(audioBuffer);
-        audioBuffer = NULL;
-        audioBufferPinned = NULL;
-    }
-}
-
-// Test for an exception and call SDL_SetError with its detail if one occurs
-// If optional parameter silent is truthy then SDL_SetError() is not called.
-static bool Android_JNI_ExceptionOccurred(bool silent = false)
-{
-    SDL_assert(LocalReferenceHolder::IsActive());
-    JNIEnv *mEnv = Android_JNI_GetEnv();
-
-    jthrowable exception = mEnv->ExceptionOccurred();
-    if (exception != NULL) {
-        jmethodID mid;
-
-        // Until this happens most JNI operations have undefined behaviour
-        mEnv->ExceptionClear();
-
-        if (!silent) {
-            jclass exceptionClass = mEnv->GetObjectClass(exception);
-            jclass classClass = mEnv->FindClass("java/lang/Class");
-
-            mid = mEnv->GetMethodID(classClass, "getName", "()Ljava/lang/String;");
-            jstring exceptionName = (jstring)mEnv->CallObjectMethod(exceptionClass, mid);
-            const char* exceptionNameUTF8 = mEnv->GetStringUTFChars(exceptionName, 0);
-
-            mid = mEnv->GetMethodID(exceptionClass, "getMessage", "()Ljava/lang/String;");
-            jstring exceptionMessage = (jstring)mEnv->CallObjectMethod(exception, mid);
-
-            if (exceptionMessage != NULL) {
-                const char* exceptionMessageUTF8 = mEnv->GetStringUTFChars(exceptionMessage, 0);
-                SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
-                mEnv->ReleaseStringUTFChars(exceptionMessage, exceptionMessageUTF8);
-            } else {
-                SDL_SetError("%s", exceptionNameUTF8);
-            }
-
-            mEnv->ReleaseStringUTFChars(exceptionName, exceptionNameUTF8);
-        }
-
-        return true;
-    }
-
-    return false;
-}
-
-static int Android_JNI_FileOpen(SDL_RWops* ctx)
-{
-    LocalReferenceHolder refs(__FUNCTION__);
-    int result = 0;
-
-    jmethodID mid;
-    jobject context;
-    jobject assetManager;
-    jobject inputStream;
-    jclass channels;
-    jobject readableByteChannel;
-    jstring fileNameJString;
-    jobject fd;
-    jclass fdCls;
-    jfieldID descriptor;
-
-    JNIEnv *mEnv = Android_JNI_GetEnv();
-    if (!refs.init(mEnv)) {
-        goto failure;
-    }
-
-    fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef;
-    ctx->hidden.androidio.position = 0;
-
-    // context = SDLActivity.getContext();
-    mid = mEnv->GetStaticMethodID(mActivityClass,
-            "getContext","()Landroid/content/Context;");
-    context = mEnv->CallStaticObjectMethod(mActivityClass, mid);
-
-
-    // assetManager = context.getAssets();
-    mid = mEnv->GetMethodID(mEnv->GetObjectClass(context),
-            "getAssets", "()Landroid/content/res/AssetManager;");
-    assetManager = mEnv->CallObjectMethod(context, mid);
-
-    /* First let's try opening the file to obtain an AssetFileDescriptor.
-    * This method reads the files directly from the APKs using standard *nix calls
-    */
-    mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
-    inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString);
-    if (Android_JNI_ExceptionOccurred(true)) {
-        goto fallback;
-    }
-
-    mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getStartOffset", "()J");
-    ctx->hidden.androidio.offset = mEnv->CallLongMethod(inputStream, mid);
-    if (Android_JNI_ExceptionOccurred(true)) {
-        goto fallback;
-    }
-
-    mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getDeclaredLength", "()J");
-    ctx->hidden.androidio.size = mEnv->CallLongMethod(inputStream, mid);
-    if (Android_JNI_ExceptionOccurred(true)) {
-        goto fallback;
-    }
-
-    mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;");
-    fd = mEnv->CallObjectMethod(inputStream, mid);
-    fdCls = mEnv->GetObjectClass(fd);
-    descriptor = mEnv->GetFieldID(fdCls, "descriptor", "I");
-    ctx->hidden.androidio.fd = mEnv->GetIntField(fd, descriptor);
-    ctx->hidden.androidio.assetFileDescriptorRef = mEnv->NewGlobalRef(inputStream);
-
-    // Seek to the correct offset in the file.
-    lseek(ctx->hidden.androidio.fd, (off_t)ctx->hidden.androidio.offset, SEEK_SET);
-
-    if (false) {
-fallback:
-        // Disabled log message because of spam on the Nexus 7
-        //__android_log_print(ANDROID_LOG_DEBUG, "SDL", "Falling back to legacy InputStream method for opening file");
-
-        /* Try the old method using InputStream */
-        ctx->hidden.androidio.assetFileDescriptorRef = NULL;
-
-        // inputStream = assetManager.open(<filename>);
-        mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager),
-                "open", "(Ljava/lang/String;I)Ljava/io/InputStream;");
-        inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString, 1 /*ACCESS_RANDOM*/);
-        if (Android_JNI_ExceptionOccurred()) {
-            goto failure;
-        }
-
-        ctx->hidden.androidio.inputStreamRef = mEnv->NewGlobalRef(inputStream);
-
-        // Despite all the visible documentation on [Asset]InputStream claiming
-        // that the .available() method is not guaranteed to return the entire file
-        // size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ...
-        // android/apis/content/ReadAsset.java imply that Android's
-        // AssetInputStream.available() /will/ always return the total file size
-
-        // size = inputStream.available();
-        mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
-                "available", "()I");
-        ctx->hidden.androidio.size = (long)mEnv->CallIntMethod(inputStream, mid);
-        if (Android_JNI_ExceptionOccurred()) {
-            goto failure;
-        }
-
-        // readableByteChannel = Channels.newChannel(inputStream);
-        channels = mEnv->FindClass("java/nio/channels/Channels");
-        mid = mEnv->GetStaticMethodID(channels,
-                "newChannel",
-                "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
-        readableByteChannel = mEnv->CallStaticObjectMethod(
-                channels, mid, inputStream);
-        if (Android_JNI_ExceptionOccurred()) {
-            goto failure;
-        }
-
-        ctx->hidden.androidio.readableByteChannelRef =
-            mEnv->NewGlobalRef(readableByteChannel);
-
-        // Store .read id for reading purposes
-        mid = mEnv->GetMethodID(mEnv->GetObjectClass(readableByteChannel),
-                "read", "(Ljava/nio/ByteBuffer;)I");
-        ctx->hidden.androidio.readMethod = mid;
-    }
-
-    if (false) {
-failure:
-        result = -1;
-
-        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
-
-        if(ctx->hidden.androidio.inputStreamRef != NULL) {
-            mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
-        }
-
-        if(ctx->hidden.androidio.readableByteChannelRef != NULL) {
-            mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef);
-        }
-
-        if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) {
-            mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.assetFileDescriptorRef);
-        }
-
-    }
-
-    return result;
-}
-
-extern "C" int Android_JNI_FileOpen(SDL_RWops* ctx,
-        const char* fileName, const char*)
-{
-    LocalReferenceHolder refs(__FUNCTION__);
-    JNIEnv *mEnv = Android_JNI_GetEnv();
-
-    if (!refs.init(mEnv)) {
-        return -1;
-    }
-
-    if (!ctx) {
-        return -1;
-    }
-
-    jstring fileNameJString = mEnv->NewStringUTF(fileName);
-    ctx->hidden.androidio.fileNameRef = mEnv->NewGlobalRef(fileNameJString);
-    ctx->hidden.androidio.inputStreamRef = NULL;
-    ctx->hidden.androidio.readableByteChannelRef = NULL;
-    ctx->hidden.androidio.readMethod = NULL;
-    ctx->hidden.androidio.assetFileDescriptorRef = NULL;
-
-    return Android_JNI_FileOpen(ctx);
-}
-
-extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
-        size_t size, size_t maxnum)
-{
-    LocalReferenceHolder refs(__FUNCTION__);
-
-    if (ctx->hidden.androidio.assetFileDescriptorRef) {
-        size_t bytesMax = size * maxnum;
-        if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) {
-            bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position;
-        }
-        size_t result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
-        if (result > 0) {
-            ctx->hidden.androidio.position += result;
-            return result / size;
-        }
-        return 0;
-    } else {
-        jlong bytesRemaining = (jlong) (size * maxnum);
-        jlong bytesMax = (jlong) (ctx->hidden.androidio.size -  ctx->hidden.androidio.position);
-        int bytesRead = 0;
-
-        /* Don't read more bytes than those that remain in the file, otherwise we get an exception */
-        if (bytesRemaining >  bytesMax) bytesRemaining = bytesMax;
-
-        JNIEnv *mEnv = Android_JNI_GetEnv();
-        if (!refs.init(mEnv)) {
-            return 0;
-        }
-
-        jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
-        jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
-        jobject byteBuffer = mEnv->NewDirectByteBuffer(buffer, bytesRemaining);
-
-        while (bytesRemaining > 0) {
-            // result = readableByteChannel.read(...);
-            int result = mEnv->CallIntMethod(readableByteChannel, readMethod, byteBuffer);
-
-            if (Android_JNI_ExceptionOccurred()) {
-                return 0;
-            }
-
-            if (result < 0) {
-                break;
-            }
-
-            bytesRemaining -= result;
-            bytesRead += result;
-            ctx->hidden.androidio.position += result;
-        }
-        return bytesRead / size;
-    }
-}
-
-extern "C" size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer,
-        size_t size, size_t num)
-{
-    SDL_SetError("Cannot write to Android package filesystem");
-    return 0;
-}
-
-static int Android_JNI_FileClose(SDL_RWops* ctx, bool release)
-{
-    LocalReferenceHolder refs(__FUNCTION__);
-    int result = 0;
-    JNIEnv *mEnv = Android_JNI_GetEnv();
-
-    if (!refs.init(mEnv)) {
-        return SDL_SetError("Failed to allocate enough JVM local references");
-    }
-
-    if (ctx) {
-        if (release) {
-            mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
-        }
-
-        if (ctx->hidden.androidio.assetFileDescriptorRef) {
-            jobject inputStream = (jobject)ctx->hidden.androidio.assetFileDescriptorRef;
-            jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
-                    "close", "()V");
-            mEnv->CallVoidMethod(inputStream, mid);
-            mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.assetFileDescriptorRef);
-            if (Android_JNI_ExceptionOccurred()) {
-                result = -1;
-            }
-        }
-        else {
-            jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef;
-
-            // inputStream.close();
-            jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
-                    "close", "()V");
-            mEnv->CallVoidMethod(inputStream, mid);
-            mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
-            mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef);
-            if (Android_JNI_ExceptionOccurred()) {
-                result = -1;
-            }
-        }
-
-        if (release) {
-            SDL_FreeRW(ctx);
-        }
-    }
-
-    return result;
-}
-
-
-extern "C" Sint64 Android_JNI_FileSize(SDL_RWops* ctx)
-{
-    return ctx->hidden.androidio.size;
-}
-
-extern "C" Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
-{
-    if (ctx->hidden.androidio.assetFileDescriptorRef) {
-        switch (whence) {
-            case RW_SEEK_SET:
-                if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
-                offset += ctx->hidden.androidio.offset;
-                break;
-            case RW_SEEK_CUR:
-                offset += ctx->hidden.androidio.position;
-                if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
-                offset += ctx->hidden.androidio.offset;
-                break;
-            case RW_SEEK_END:
-                offset = ctx->hidden.androidio.offset + ctx->hidden.androidio.size + offset;
-                break;
-            default:
-                return SDL_SetError("Unknown value for 'whence'");
-        }
-        whence = SEEK_SET;
-
-        off_t ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
-        if (ret == -1) return -1;
-        ctx->hidden.androidio.position = ret - ctx->hidden.androidio.offset;
-    } else {
-        Sint64 newPosition;
-
-        switch (whence) {
-            case RW_SEEK_SET:
-                newPosition = offset;
-                break;
-            case RW_SEEK_CUR:
-                newPosition = ctx->hidden.androidio.position + offset;
-                break;
-            case RW_SEEK_END:
-                newPosition = ctx->hidden.androidio.size + offset;
-                break;
-            default:
-                return SDL_SetError("Unknown value for 'whence'");
-        }
-
-        /* Validate the new position */
-        if (newPosition < 0) {
-            return SDL_Error(SDL_EFSEEK);
-        }
-        if (newPosition > ctx->hidden.androidio.size) {
-            newPosition = ctx->hidden.androidio.size;
-        }
-
-        Sint64 movement = newPosition - ctx->hidden.androidio.position;
-        if (movement > 0) {
-            unsigned char buffer[4096];
-
-            // The easy case where we're seeking forwards
-            while (movement > 0) {
-                Sint64 amount = sizeof (buffer);
-                if (amount > movement) {
-                    amount = movement;
-                }
-                size_t result = Android_JNI_FileRead(ctx, buffer, 1, amount);
-                if (result <= 0) {
-                    // Failed to read/skip the required amount, so fail
-                    return -1;
-                }
-
-                movement -= result;
-            }
-
-        } else if (movement < 0) {
-            // We can't seek backwards so we have to reopen the file and seek
-            // forwards which obviously isn't very efficient
-            Android_JNI_FileClose(ctx, false);
-            Android_JNI_FileOpen(ctx);
-            Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
-        }
-    }
-
-    return ctx->hidden.androidio.position;
-
-}
-
-extern "C" int Android_JNI_FileClose(SDL_RWops* ctx)
-{
-    return Android_JNI_FileClose(ctx, true);
-}
-
-// returns a new global reference which needs to be released later
-static jobject Android_JNI_GetSystemServiceObject(const char* name)
-{
-    LocalReferenceHolder refs(__FUNCTION__);
-    JNIEnv* env = Android_JNI_GetEnv();
-    if (!refs.init(env)) {
-        return NULL;
-    }
-
-    jstring service = env->NewStringUTF(name);
-
-    jmethodID mid;
-
-    mid = env->GetStaticMethodID(mActivityClass, "getContext", "()Landroid/content/Context;");
-    jobject context = env->CallStaticObjectMethod(mActivityClass, mid);
-
-    mid = env->GetMethodID(mActivityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
-    jobject manager = env->CallObjectMethod(context, mid, service);
-
-    env->DeleteLocalRef(service);
-
-    return manager ? env->NewGlobalRef(manager) : NULL;
-}
-
-#define SETUP_CLIPBOARD(error) \
-    LocalReferenceHolder refs(__FUNCTION__); \
-    JNIEnv* env = Android_JNI_GetEnv(); \
-    if (!refs.init(env)) { \
-        return error; \
-    } \
-    jobject clipboard = Android_JNI_GetSystemServiceObject("clipboard"); \
-    if (!clipboard) { \
-        return error; \
-    }
-
-extern "C" int Android_JNI_SetClipboardText(const char* text)
-{
-    SETUP_CLIPBOARD(-1)
-
-    jmethodID mid = env->GetMethodID(env->GetObjectClass(clipboard), "setText", "(Ljava/lang/CharSequence;)V");
-    jstring string = env->NewStringUTF(text);
-    env->CallVoidMethod(clipboard, mid, string);
-    env->DeleteGlobalRef(clipboard);
-    env->DeleteLocalRef(string);
-    return 0;
-}
-
-extern "C" char* Android_JNI_GetClipboardText()
-{
-    SETUP_CLIPBOARD(SDL_strdup(""))
-
-    jmethodID mid = env->GetMethodID(env->GetObjectClass(clipboard), "getText", "()Ljava/lang/CharSequence;");
-    jobject sequence = env->CallObjectMethod(clipboard, mid);
-    env->DeleteGlobalRef(clipboard);
-    if (sequence) {
-        mid = env->GetMethodID(env->GetObjectClass(sequence), "toString", "()Ljava/lang/String;");
-        jstring string = reinterpret_cast<jstring>(env->CallObjectMethod(sequence, mid));
-        const char* utf = env->GetStringUTFChars(string, 0);
-        if (utf) {
-            char* text = SDL_strdup(utf);
-            env->ReleaseStringUTFChars(string, utf);
-            return text;
-        }
-    }
-    return SDL_strdup("");
-}
-
-extern "C" SDL_bool Android_JNI_HasClipboardText()
-{
-    SETUP_CLIPBOARD(SDL_FALSE)
-
-    jmethodID mid = env->GetMethodID(env->GetObjectClass(clipboard), "hasText", "()Z");
-    jboolean has = env->CallBooleanMethod(clipboard, mid);
-    env->DeleteGlobalRef(clipboard);
-    return has ? SDL_TRUE : SDL_FALSE;
-}
-
-
-// returns 0 on success or -1 on error (others undefined then)
-// returns truthy or falsy value in plugged, charged and battery
-// returns the value in seconds and percent or -1 if not available
-extern "C" int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent)
-{
-    LocalReferenceHolder refs(__FUNCTION__);
-    JNIEnv* env = Android_JNI_GetEnv();
-    if (!refs.init(env)) {
-        return -1;
-    }
-
-    jmethodID mid;
-
-    mid = env->GetStaticMethodID(mActivityClass, "getContext", "()Landroid/content/Context;");
-    jobject context = env->CallStaticObjectMethod(mActivityClass, mid);
-
-    jstring action = env->NewStringUTF("android.intent.action.BATTERY_CHANGED");
-
-    jclass cls = env->FindClass("android/content/IntentFilter");
-
-    mid = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V");
-    jobject filter = env->NewObject(cls, mid, action);
-
-    env->DeleteLocalRef(action);
-
-    mid = env->GetMethodID(mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
-    jobject intent = env->CallObjectMethod(context, mid, NULL, filter);
-
-    env->DeleteLocalRef(filter);
-
-    cls = env->GetObjectClass(intent);
-
-    jstring iname;
-    jmethodID imid = env->GetMethodID(cls, "getIntExtra", "(Ljava/lang/String;I)I");
-
-#define GET_INT_EXTRA(var, key) \
-    iname = env->NewStringUTF(key); \
-    int var = env->CallIntMethod(intent, imid, iname, -1); \
-    env->DeleteLocalRef(iname);
-
-    jstring bname;
-    jmethodID bmid = env->GetMethodID(cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
-
-#define GET_BOOL_EXTRA(var, key) \
-    bname = env->NewStringUTF(key); \
-    int var = env->CallBooleanMethod(intent, bmid, bname, JNI_FALSE); \
-    env->DeleteLocalRef(bname);
-
-    if (plugged) {
-        GET_INT_EXTRA(plug, "plugged") // == BatteryManager.EXTRA_PLUGGED (API 5)
-        if (plug == -1) {
-            return -1;
-        }
-        // 1 == BatteryManager.BATTERY_PLUGGED_AC
-        // 2 == BatteryManager.BATTERY_PLUGGED_USB
-        *plugged = (0 < plug) ? 1 : 0;
-    }
-
-    if (charged) {
-        GET_INT_EXTRA(status, "status") // == BatteryManager.EXTRA_STATUS (API 5)
-        if (status == -1) {
-            return -1;
-        }
-        // 5 == BatteryManager.BATTERY_STATUS_FULL
-        *charged = (status == 5) ? 1 : 0;
-    }
-
-    if (battery) {
-        GET_BOOL_EXTRA(present, "present") // == BatteryManager.EXTRA_PRESENT (API 5)
-        *battery = present ? 1 : 0;
-    }
-
-    if (seconds) {
-        *seconds = -1; // not possible
-    }
-
-    if (percent) {
-        GET_INT_EXTRA(level, "level") // == BatteryManager.EXTRA_LEVEL (API 5)
-        GET_INT_EXTRA(scale, "scale") // == BatteryManager.EXTRA_SCALE (API 5)
-        if ((level == -1) || (scale == -1)) {
-            return -1;
-        }
-        *percent = level * 100 / scale;
-    }
-
-    env->DeleteLocalRef(intent);
-
-    return 0;
-}
-
-// sends message to be handled on the UI event dispatch thread
-extern "C" int Android_JNI_SendMessage(int command, int param)
-{
-    JNIEnv *env = Android_JNI_GetEnv();
-    if (!env) {
-        return -1;
-    }
-    jmethodID mid = env->GetStaticMethodID(mActivityClass, "sendMessage", "(II)Z");
-    if (!mid) {
-        return -1;
-    }
-    jboolean success = env->CallStaticBooleanMethod(mActivityClass, mid, command, param);
-    return success ? 0 : -1;
-}
-
-extern "C" void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
-{
-    JNIEnv *env = Android_JNI_GetEnv();
-    if (!env) {
-        return;
-    }
-
-    jmethodID mid = env->GetStaticMethodID(mActivityClass, "showTextInput", "(IIII)Z");
-    if (!mid) {
-        return;
-    }
-    env->CallStaticBooleanMethod( mActivityClass, mid,
-                               inputRect->x,
-                               inputRect->y,
-                               inputRect->w,
-                               inputRect->h );
-}
-
-extern "C" void Android_JNI_HideTextInput()
-{
-    // has to match Activity constant
-    const int COMMAND_TEXTEDIT_HIDE = 3;
-    Android_JNI_SendMessage(COMMAND_TEXTEDIT_HIDE, 0);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Functions exposed to SDL applications in SDL_system.h
-//
-
-extern "C" void *SDL_AndroidGetJNIEnv()
-{
-    return Android_JNI_GetEnv();
-}
-
-
-
-extern "C" void *SDL_AndroidGetActivity()
-{
-    /* See SDL_system.h for caveats on using this function. */
-
-    jmethodID mid;
-
-    JNIEnv *env = Android_JNI_GetEnv();
-    if (!env) {
-        return NULL;
-    }
-
-    // return SDLActivity.getContext();
-    mid = env->GetStaticMethodID(mActivityClass,
-            "getContext","()Landroid/content/Context;");
-    return env->CallStaticObjectMethod(mActivityClass, mid);
-}
-
-extern "C" const char * SDL_AndroidGetInternalStoragePath()
-{
-    static char *s_AndroidInternalFilesPath = NULL;
-
-    if (!s_AndroidInternalFilesPath) {
-        LocalReferenceHolder refs(__FUNCTION__);
-        jmethodID mid;
-        jobject context;
-        jobject fileObject;
-        jstring pathString;
-        const char *path;
-
-        JNIEnv *env = Android_JNI_GetEnv();
-        if (!refs.init(env)) {
-            return NULL;
-        }
-
-        // context = SDLActivity.getContext();
-        mid = env->GetStaticMethodID(mActivityClass,
-                "getContext","()Landroid/content/Context;");
-        context = env->CallStaticObjectMethod(mActivityClass, mid);
-
-        // fileObj = context.getFilesDir();
-        mid = env->GetMethodID(env->GetObjectClass(context),
-                "getFilesDir", "()Ljava/io/File;");
-        fileObject = env->CallObjectMethod(context, mid);
-        if (!fileObject) {
-            SDL_SetError("Couldn't get internal directory");
-            return NULL;
-        }
-
-        // path = fileObject.getAbsolutePath();
-        mid = env->GetMethodID(env->GetObjectClass(fileObject),
-                "getAbsolutePath", "()Ljava/lang/String;");
-        pathString = (jstring)env->CallObjectMethod(fileObject, mid);
-
-        path = env->GetStringUTFChars(pathString, NULL);
-        s_AndroidInternalFilesPath = SDL_strdup(path);
-        env->ReleaseStringUTFChars(pathString, path);
-    }
-    return s_AndroidInternalFilesPath;
-}
-
-extern "C" int SDL_AndroidGetExternalStorageState()
-{
-    LocalReferenceHolder refs(__FUNCTION__);
-    jmethodID mid;
-    jclass cls;
-    jstring stateString;
-    const char *state;
-    int stateFlags;
-
-    JNIEnv *env = Android_JNI_GetEnv();
-    if (!refs.init(env)) {
-        return 0;
-    }
-
-    cls = env->FindClass("android/os/Environment");
-    mid = env->GetStaticMethodID(cls,
-            "getExternalStorageState", "()Ljava/lang/String;");
-    stateString = (jstring)env->CallStaticObjectMethod(cls, mid);
-
-    state = env->GetStringUTFChars(stateString, NULL);
-
-    // Print an info message so people debugging know the storage state
-    __android_log_print(ANDROID_LOG_INFO, "SDL", "external storage state: %s", state);
-
-    if (SDL_strcmp(state, "mounted") == 0) {
-        stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ |
-                     SDL_ANDROID_EXTERNAL_STORAGE_WRITE;
-    } else if (SDL_strcmp(state, "mounted_ro") == 0) {
-        stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ;
-    } else {
-        stateFlags = 0;
-    }
-    env->ReleaseStringUTFChars(stateString, state);
-
-    return stateFlags;
-}
-
-extern "C" const char * SDL_AndroidGetExternalStoragePath()
-{
-    static char *s_AndroidExternalFilesPath = NULL;
-
-    if (!s_AndroidExternalFilesPath) {
-        LocalReferenceHolder refs(__FUNCTION__);
-        jmethodID mid;
-        jobject context;
-        jobject fileObject;
-        jstring pathString;
-        const char *path;
-
-        JNIEnv *env = Android_JNI_GetEnv();
-        if (!refs.init(env)) {
-            return NULL;
-        }
-
-        // context = SDLActivity.getContext();
-        mid = env->GetStaticMethodID(mActivityClass,
-                "getContext","()Landroid/content/Context;");
-        context = env->CallStaticObjectMethod(mActivityClass, mid);
-
-        // fileObj = context.getExternalFilesDir();
-        mid = env->GetMethodID(env->GetObjectClass(context),
-                "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
-        fileObject = env->CallObjectMethod(context, mid, NULL);
-        if (!fileObject) {
-            SDL_SetError("Couldn't get external directory");
-            return NULL;
-        }
-
-        // path = fileObject.getAbsolutePath();
-        mid = env->GetMethodID(env->GetObjectClass(fileObject),
-                "getAbsolutePath", "()Ljava/lang/String;");
-        pathString = (jstring)env->CallObjectMethod(fileObject, mid);
-
-        path = env->GetStringUTFChars(pathString, NULL);
-        s_AndroidExternalFilesPath = SDL_strdup(path);
-        env->ReleaseStringUTFChars(pathString, path);
-    }
-    return s_AndroidExternalFilesPath;
-}
-
-#endif /* __ANDROID__ */
-
-/* vi: set ts=4 sw=4 expandtab: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/android/SDL_android_main.c	Mon Jul 22 02:51:45 2013 -0700
@@ -0,0 +1,38 @@
+
+#include "SDL_config.h"
+
+#ifdef __ANDROID__
+
+/* Include the SDL main definition header */
+#include "SDL_main.h"
+
+/*******************************************************************************
+                 Functions called by JNI
+*******************************************************************************/
+#include <jni.h>
+
+// Called before SDL_main() to initialize JNI bindings in SDL library
+extern void SDL_Android_Init(JNIEnv* env, jclass cls);
+
+// Start up the SDL app
+void Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj)
+{
+    /* This interface could expand with ABI negotiation, calbacks, etc. */
+    SDL_Android_Init(env, cls);
+
+    SDL_SetMainReady();
+
+    /* Run the application code! */
+    int status;
+    char *argv[2];
+    argv[0] = SDL_strdup("SDL_app");
+    argv[1] = NULL;
+    status = SDL_main(1, argv);
+
+    /* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
+    //exit(status);
+}
+
+#endif /* __ANDROID__ */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- a/src/main/android/SDL_android_main.cpp	Mon Jul 22 22:54:00 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-
-#include "SDL_config.h"
-
-#ifdef __ANDROID__
-
-/* Include the SDL main definition header */
-#include "SDL_main.h"
-
-/*******************************************************************************
-                 Functions called by JNI
-*******************************************************************************/
-#include <jni.h>
-
-// Called before SDL_main() to initialize JNI bindings in SDL library
-extern "C" void SDL_Android_Init(JNIEnv* env, jclass cls);
-
-// Start up the SDL app
-extern "C" void Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj)
-{
-    /* This interface could expand with ABI negotiation, calbacks, etc. */
-    SDL_Android_Init(env, cls);
-
-    SDL_SetMainReady();
-
-    /* Run the application code! */
-    int status;
-    char *argv[2];
-    argv[0] = SDL_strdup("SDL_app");
-    argv[1] = NULL;
-    status = SDL_main(1, argv);
-
-    /* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
-    //exit(status);
-}
-
-#endif /* __ANDROID__ */
-
-/* vi: set ts=4 sw=4 expandtab: */