src/core/android/SDL_android.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Sun, 27 Sep 2015 20:12:47 +0200
changeset 9880 3bbeb623181c
parent 9873 b0f121cfa074
child 9998 f67cf37e9cd4
permissions -rw-r--r--
Android: Removed three internal functions not used by SDL.

They were not needed internally since the switch to the common EGL backend.

Thanks to the SDL mailing list for pointing out that the functions seem unused.
slouken@4964
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@9619
     3
  Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
slouken@4964
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@4964
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@4964
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@4964
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@5222
    22
#include "SDL_stdinc.h"
slouken@6284
    23
#include "SDL_assert.h"
slouken@8961
    24
#include "SDL_hints.h"
slouken@6650
    25
#include "SDL_log.h"
slouken@4964
    26
slouken@6044
    27
#ifdef __ANDROID__
slouken@6044
    28
slouken@6630
    29
#include "SDL_system.h"
slouken@4989
    30
#include "SDL_android.h"
slouken@6792
    31
#include <EGL/egl.h>
slouken@4989
    32
slouken@5092
    33
#include "../../events/SDL_events_c.h"
slouken@5092
    34
#include "../../video/android/SDL_androidkeyboard.h"
joseba@9438
    35
#include "../../video/android/SDL_androidmouse.h"
slouken@5092
    36
#include "../../video/android/SDL_androidtouch.h"
slouken@5092
    37
#include "../../video/android/SDL_androidvideo.h"
gabomdq@7659
    38
#include "../../video/android/SDL_androidwindow.h"
gabomdq@8057
    39
#include "../../joystick/android/SDL_sysjoystick_c.h"
slouken@4995
    40
icculus@5996
    41
#include <android/log.h>
gabomdq@6354
    42
#include <pthread.h>
gabomdq@6806
    43
#include <sys/types.h>
gabomdq@6806
    44
#include <unistd.h>
icculus@5996
    45
#define LOG_TAG "SDL_android"
gabomdq@7678
    46
/* #define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) */
gabomdq@7678
    47
/* #define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) */
philipp@9271
    48
#define LOGI(...) do {} while (0)
philipp@9271
    49
#define LOGE(...) do {} while (0)
icculus@5996
    50
slouken@6650
    51
/* Uncomment this to log messages entering and exiting methods in this file */
gabomdq@7678
    52
/* #define DEBUG_JNI */
icculus@5996
    53
philipp@7246
    54
static void Android_JNI_ThreadDestroyed(void*);
slouken@4980
    55
slouken@4964
    56
/*******************************************************************************
slouken@4964
    57
 This file links the Java side of Android with libsdl
slouken@4964
    58
*******************************************************************************/
slouken@4964
    59
#include <jni.h>
slouken@4964
    60
#include <android/log.h>
slouken@4964
    61
slouken@4964
    62
slouken@4964
    63
/*******************************************************************************
slouken@4964
    64
                               Globals
slouken@4964
    65
*******************************************************************************/
gabomdq@6354
    66
static pthread_key_t mThreadKey;
icculus@5996
    67
static JavaVM* mJavaVM;
slouken@4964
    68
gabomdq@7663
    69
/* Main activity */
slouken@4998
    70
static jclass mActivityClass;
slouken@4964
    71
gabomdq@7663
    72
/* method signatures */
gabomdq@7659
    73
static jmethodID midGetNativeSurface;
slouken@4995
    74
static jmethodID midAudioInit;
slouken@4995
    75
static jmethodID midAudioWriteShortBuffer;
slouken@4995
    76
static jmethodID midAudioWriteByteBuffer;
slouken@4996
    77
static jmethodID midAudioQuit;
gabomdq@8057
    78
static jmethodID midPollInputDevices;
slouken@4964
    79
gabomdq@7663
    80
/* Accelerometer data storage */
slouken@5000
    81
static float fLastAccelerometer[3];
philipp@9271
    82
static SDL_bool bHasNewData;
slouken@4964
    83
slouken@4964
    84
/*******************************************************************************
slouken@4964
    85
                 Functions called by JNI
slouken@4964
    86
*******************************************************************************/
slouken@4964
    87
gabomdq@7663
    88
/* Library init */
dimitris@8760
    89
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
slouken@4964
    90
{
icculus@5996
    91
    JNIEnv *env;
icculus@5996
    92
    mJavaVM = vm;
icculus@5996
    93
    LOGI("JNI_OnLoad called");
ewing@7501
    94
    if ((*mJavaVM)->GetEnv(mJavaVM, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
icculus@5996
    95
        LOGE("Failed to get the environment using GetEnv()");
icculus@5996
    96
        return -1;
icculus@5996
    97
    }
gabomdq@6354
    98
    /*
gabomdq@6354
    99
     * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
gabomdq@6354
   100
     * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
gabomdq@6354
   101
     */
slouken@8055
   102
    if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed) != 0) {
gabomdq@6354
   103
        __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing pthread key");
gabomdq@6354
   104
    }
slouken@8055
   105
    Android_JNI_SetupThread();
icculus@5996
   106
slouken@4964
   107
    return JNI_VERSION_1_4;
slouken@4964
   108
}
slouken@4964
   109
gabomdq@7663
   110
/* Called before SDL_main() to initialize JNI bindings */
dimitris@8760
   111
JNIEXPORT void JNICALL SDL_Android_Init(JNIEnv* mEnv, jclass cls)
slouken@4964
   112
{
slouken@4964
   113
    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init()");
slouken@4964
   114
gabomdq@6354
   115
    Android_JNI_SetupThread();
gabomdq@6354
   116
ewing@7501
   117
    mActivityClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
slouken@4998
   118
gabomdq@7659
   119
    midGetNativeSurface = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
gabomdq@7659
   120
                                "getNativeSurface","()Landroid/view/Surface;");
ewing@7501
   121
    midAudioInit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
gabomdq@7552
   122
                                "audioInit", "(IZZI)I");
ewing@7501
   123
    midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
slouken@4998
   124
                                "audioWriteShortBuffer", "([S)V");
ewing@7501
   125
    midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
slouken@4998
   126
                                "audioWriteByteBuffer", "([B)V");
ewing@7501
   127
    midAudioQuit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
slouken@4998
   128
                                "audioQuit", "()V");
gabomdq@8057
   129
    midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
gabomdq@8057
   130
                                "pollInputDevices", "()V");
slouken@4964
   131
philipp@9271
   132
    bHasNewData = SDL_FALSE;
slouken@6212
   133
philipp@9880
   134
    if (!midGetNativeSurface || !midAudioInit ||
gabomdq@8057
   135
       !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit || !midPollInputDevices) {
slouken@4996
   136
        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
slouken@4964
   137
    }
icculus@5996
   138
    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init() finished!");
slouken@4964
   139
}
slouken@4964
   140
icculus@9640
   141
/* Drop file */
icculus@9640
   142
void Java_org_libsdl_app_SDLActivity_onNativeDropFile(
icculus@9640
   143
                                    JNIEnv* env, jclass jcls,
icculus@9640
   144
                                    jstring filename)
icculus@9640
   145
{
icculus@9640
   146
    const char *path = (*env)->GetStringUTFChars(env, filename, NULL);
icculus@9640
   147
    SDL_SendDropFile(path);
icculus@9640
   148
    (*env)->ReleaseStringUTFChars(env, filename, path);
icculus@9640
   149
}
icculus@9640
   150
gabomdq@7663
   151
/* Resize */
dimitris@8760
   152
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeResize(
slouken@4998
   153
                                    JNIEnv* env, jclass jcls,
philipp@9314
   154
                                    jint width, jint height, jint format, jfloat rate)
slouken@4995
   155
{
philipp@9314
   156
    Android_SetScreenResolution(width, height, format, rate);
slouken@4995
   157
}
slouken@4995
   158
philipp@8776
   159
/* Paddown */
philipp@9858
   160
JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_onNativePadDown(
gabomdq@7907
   161
                                    JNIEnv* env, jclass jcls,
gabomdq@8057
   162
                                    jint device_id, jint keycode)
gabomdq@7907
   163
{
gabomdq@8057
   164
    return Android_OnPadDown(device_id, keycode);
gabomdq@7907
   165
}
gabomdq@7907
   166
philipp@8776
   167
/* Padup */
philipp@9858
   168
JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_onNativePadUp(
gabomdq@7907
   169
                                   JNIEnv* env, jclass jcls,
gabomdq@8057
   170
                                   jint device_id, jint keycode)
gabomdq@7907
   171
{
gabomdq@8057
   172
    return Android_OnPadUp(device_id, keycode);
gabomdq@7907
   173
}
gabomdq@7907
   174
philipp@7926
   175
/* Joy */
dimitris@8760
   176
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeJoy(
gabomdq@7907
   177
                                    JNIEnv* env, jclass jcls,
gabomdq@8057
   178
                                    jint device_id, jint axis, jfloat value)
gabomdq@7907
   179
{
gabomdq@8057
   180
    Android_OnJoy(device_id, axis, value);
gabomdq@8057
   181
}
gabomdq@8057
   182
dbrady@8140
   183
/* POV Hat */
dimitris@8760
   184
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeHat(
dbrady@8140
   185
                                    JNIEnv* env, jclass jcls,
dbrady@8140
   186
                                    jint device_id, jint hat_id, jint x, jint y)
dbrady@8140
   187
{
dbrady@8140
   188
    Android_OnHat(device_id, hat_id, x, y);
dbrady@8140
   189
}
dbrady@8140
   190
gabomdq@8057
   191
philipp@9858
   192
JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_nativeAddJoystick(
gabomdq@8057
   193
    JNIEnv* env, jclass jcls,
gabomdq@8057
   194
    jint device_id, jstring device_name, jint is_accelerometer, 
gabomdq@8057
   195
    jint nbuttons, jint naxes, jint nhats, jint nballs)
gabomdq@8057
   196
{
gabomdq@8057
   197
    int retval;
gabomdq@8057
   198
    const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
gabomdq@8057
   199
gabomdq@8057
   200
    retval = Android_AddJoystick(device_id, name, (SDL_bool) is_accelerometer, nbuttons, naxes, nhats, nballs);
gabomdq@8057
   201
gabomdq@8057
   202
    (*env)->ReleaseStringUTFChars(env, device_name, name);
gabomdq@8057
   203
    
gabomdq@8057
   204
    return retval;
gabomdq@8057
   205
}
gabomdq@8057
   206
philipp@9858
   207
JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_nativeRemoveJoystick(
gabomdq@8057
   208
    JNIEnv* env, jclass jcls, jint device_id)
gabomdq@8057
   209
{
gabomdq@8057
   210
    return Android_RemoveJoystick(device_id);
gabomdq@7907
   211
}
gabomdq@7907
   212
gabomdq@7659
   213
gabomdq@7663
   214
/* Surface Created */
dimitris@8760
   215
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeSurfaceChanged(JNIEnv* env, jclass jcls)
gabomdq@7659
   216
{
gabomdq@7659
   217
    SDL_WindowData *data;
gabomdq@7659
   218
    SDL_VideoDevice *_this;
gabomdq@7659
   219
gabomdq@7659
   220
    if (Android_Window == NULL || Android_Window->driverdata == NULL ) {
gabomdq@7659
   221
        return;
gabomdq@7659
   222
    }
gabomdq@7659
   223
    
gabomdq@7659
   224
    _this =  SDL_GetVideoDevice();
gabomdq@7659
   225
    data =  (SDL_WindowData *) Android_Window->driverdata;
gabomdq@7659
   226
    
gabomdq@7659
   227
    /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
gabomdq@7659
   228
    if (data->egl_surface == EGL_NO_SURFACE) {
gabomdq@7659
   229
        if(data->native_window) {
gabomdq@7659
   230
            ANativeWindow_release(data->native_window);
gabomdq@7659
   231
        }
gabomdq@7659
   232
        data->native_window = Android_JNI_GetNativeWindow();
gabomdq@7659
   233
        data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window);
gabomdq@7659
   234
    }
gabomdq@7659
   235
    
gabomdq@7659
   236
    /* GL Context handling is done in the event loop because this function is run from the Java thread */
gabomdq@7659
   237
    
gabomdq@7659
   238
}
gabomdq@7659
   239
gabomdq@7663
   240
/* Surface Destroyed */
dimitris@8760
   241
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeSurfaceDestroyed(JNIEnv* env, jclass jcls)
gabomdq@7659
   242
{
gabomdq@7659
   243
    /* We have to clear the current context and destroy the egl surface here
gabomdq@7659
   244
     * Otherwise there's BAD_NATIVE_WINDOW errors coming from eglCreateWindowSurface on resume
gabomdq@7659
   245
     * Ref: http://stackoverflow.com/questions/8762589/eglcreatewindowsurface-on-ics-and-switching-from-2d-to-3d
gabomdq@7659
   246
     */
gabomdq@7659
   247
    SDL_WindowData *data;
gabomdq@7659
   248
    SDL_VideoDevice *_this;
gabomdq@7659
   249
    
gabomdq@7659
   250
    if (Android_Window == NULL || Android_Window->driverdata == NULL ) {
gabomdq@7659
   251
        return;
gabomdq@7659
   252
    }
gabomdq@7659
   253
    
gabomdq@7659
   254
    _this =  SDL_GetVideoDevice();
gabomdq@7659
   255
    data = (SDL_WindowData *) Android_Window->driverdata;
gabomdq@7659
   256
    
gabomdq@7659
   257
    if (data->egl_surface != EGL_NO_SURFACE) {
gabomdq@7659
   258
        SDL_EGL_MakeCurrent(_this, NULL, NULL);
gabomdq@7659
   259
        SDL_EGL_DestroySurface(_this, data->egl_surface);
gabomdq@7659
   260
        data->egl_surface = EGL_NO_SURFACE;
gabomdq@7659
   261
    }
gabomdq@7659
   262
    
gabomdq@7659
   263
    /* GL Context handling is done in the event loop because this function is run from the Java thread */
gabomdq@7659
   264
gabomdq@7659
   265
}
gabomdq@7659
   266
gabomdq@7663
   267
/* Keydown */
dimitris@8760
   268
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeKeyDown(
slouken@4998
   269
                                    JNIEnv* env, jclass jcls, jint keycode)
slouken@4964
   270
{
slouken@4980
   271
    Android_OnKeyDown(keycode);
slouken@4964
   272
}
slouken@4964
   273
gabomdq@7663
   274
/* Keyup */
dimitris@8760
   275
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeKeyUp(
slouken@4998
   276
                                    JNIEnv* env, jclass jcls, jint keycode)
slouken@4964
   277
{
slouken@4980
   278
    Android_OnKeyUp(keycode);
slouken@4964
   279
}
slouken@4964
   280
gabomdq@7663
   281
/* Keyboard Focus Lost */
dimitris@8760
   282
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeKeyboardFocusLost(
gabomdq@7564
   283
                                    JNIEnv* env, jclass jcls)
gabomdq@7564
   284
{
gabomdq@7564
   285
    /* Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget */
gabomdq@7564
   286
    SDL_StopTextInput();
gabomdq@7564
   287
}
gabomdq@7564
   288
gabomdq@7564
   289
gabomdq@7663
   290
/* Touch */
dimitris@8760
   291
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeTouch(
slouken@4998
   292
                                    JNIEnv* env, jclass jcls,
icculus@5982
   293
                                    jint touch_device_id_in, jint pointer_finger_id_in,
slouken@4995
   294
                                    jint action, jfloat x, jfloat y, jfloat p)
slouken@4964
   295
{
icculus@5982
   296
    Android_OnTouch(touch_device_id_in, pointer_finger_id_in, action, x, y, p);
slouken@4964
   297
}
slouken@4964
   298
joseba@9438
   299
/* Mouse */
joseba@9438
   300
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeMouse(
joseba@9438
   301
                                    JNIEnv* env, jclass jcls,
joseba@9438
   302
                                    jint button, jint action, jfloat x, jfloat y)
joseba@9438
   303
{
joseba@9438
   304
    Android_OnMouse(button, action, x, y);
joseba@9438
   305
}
joseba@9438
   306
gabomdq@7663
   307
/* Accelerometer */
dimitris@8760
   308
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeAccel(
slouken@4998
   309
                                    JNIEnv* env, jclass jcls,
slouken@4995
   310
                                    jfloat x, jfloat y, jfloat z)
slouken@4964
   311
{
slouken@4964
   312
    fLastAccelerometer[0] = x;
slouken@4964
   313
    fLastAccelerometer[1] = y;
slouken@6212
   314
    fLastAccelerometer[2] = z;
philipp@9271
   315
    bHasNewData = SDL_TRUE;
slouken@4964
   316
}
slouken@4964
   317
gabomdq@7663
   318
/* Low memory */
dimitris@8760
   319
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_nativeLowMemory(
slouken@7190
   320
                                    JNIEnv* env, jclass cls)
slouken@7191
   321
{
slouken@7190
   322
    SDL_SendAppEvent(SDL_APP_LOWMEMORY);
slouken@7190
   323
}
slouken@7190
   324
gabomdq@7663
   325
/* Quit */
dimitris@8760
   326
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_nativeQuit(
slouken@4998
   327
                                    JNIEnv* env, jclass cls)
slouken@7191
   328
{
gabomdq@7910
   329
    /* Discard previous events. The user should have handled state storage
gabomdq@7910
   330
     * in SDL_APP_WILLENTERBACKGROUND. After nativeQuit() is called, no
gabomdq@7910
   331
     * events other than SDL_QUIT and SDL_APP_TERMINATING should fire */
gabomdq@7910
   332
    SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
gabomdq@7663
   333
    /* Inject a SDL_QUIT event */
slouken@4995
   334
    SDL_SendQuit();
slouken@7190
   335
    SDL_SendAppEvent(SDL_APP_TERMINATING);
gabomdq@7910
   336
    /* Resume the event loop so that the app can catch SDL_QUIT which
gabomdq@7910
   337
     * should now be the top event in the event queue. */
gabomdq@7910
   338
    if (!SDL_SemValue(Android_ResumeSem)) SDL_SemPost(Android_ResumeSem);
slouken@4995
   339
}
slouken@4995
   340
gabomdq@7663
   341
/* Pause */
dimitris@8760
   342
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_nativePause(
slouken@6186
   343
                                    JNIEnv* env, jclass cls)
slouken@6186
   344
{
gabomdq@8039
   345
    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
slouken@6186
   346
    if (Android_Window) {
slouken@6186
   347
        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
slouken@6191
   348
        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
gabomdq@8039
   349
        SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
gabomdq@8039
   350
        SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
gabomdq@8039
   351
    
gabomdq@8039
   352
        /* *After* sending the relevant events, signal the pause semaphore 
gabomdq@8039
   353
         * so the event loop knows to pause and (optionally) block itself */
gabomdq@8039
   354
        if (!SDL_SemValue(Android_PauseSem)) SDL_SemPost(Android_PauseSem);
slouken@6186
   355
    }
slouken@6186
   356
}
slouken@6186
   357
gabomdq@7663
   358
/* Resume */
dimitris@8760
   359
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_nativeResume(
slouken@6186
   360
                                    JNIEnv* env, jclass cls)
slouken@6186
   361
{
slouken@7190
   362
    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()");
slouken@7190
   363
slouken@6186
   364
    if (Android_Window) {
gabomdq@8047
   365
        SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
gabomdq@8047
   366
        SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
gabomdq@8047
   367
        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
gabomdq@8047
   368
        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0);
gabomdq@6330
   369
        /* Signal the resume semaphore so the event loop knows to resume and restore the GL Context
gabomdq@6330
   370
         * We can't restore the GL Context here because it needs to be done on the SDL main thread
gabomdq@6330
   371
         * and this function will be called from the Java thread instead.
gabomdq@6330
   372
         */
gabomdq@6330
   373
        if (!SDL_SemValue(Android_ResumeSem)) SDL_SemPost(Android_ResumeSem);
slouken@6186
   374
    }
slouken@6186
   375
}
slouken@6186
   376
dimitris@8760
   377
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLInputConnection_nativeCommitText(
slouken@6555
   378
                                    JNIEnv* env, jclass cls,
slouken@6555
   379
                                    jstring text, jint newCursorPosition)
slouken@6555
   380
{
ewing@7501
   381
    const char *utftext = (*env)->GetStringUTFChars(env, text, NULL);
slouken@6555
   382
slouken@6555
   383
    SDL_SendKeyboardText(utftext);
slouken@6555
   384
ewing@7501
   385
    (*env)->ReleaseStringUTFChars(env, text, utftext);
slouken@6555
   386
}
slouken@6555
   387
dimitris@8760
   388
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLInputConnection_nativeSetComposingText(
slouken@6555
   389
                                    JNIEnv* env, jclass cls,
slouken@6555
   390
                                    jstring text, jint newCursorPosition)
slouken@6555
   391
{
ewing@7501
   392
    const char *utftext = (*env)->GetStringUTFChars(env, text, NULL);
slouken@6555
   393
slouken@6555
   394
    SDL_SendEditingText(utftext, 0, 0);
slouken@6555
   395
ewing@7501
   396
    (*env)->ReleaseStringUTFChars(env, text, utftext);
slouken@6555
   397
}
slouken@6555
   398
philipp@9268
   399
JNIEXPORT jstring JNICALL Java_org_libsdl_app_SDLActivity_nativeGetHint(JNIEnv* env, jclass cls, jstring name) {
alexey@8896
   400
    const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
alexey@8896
   401
    const char *hint = SDL_GetHint(utfname);
slouken@6555
   402
alexey@8896
   403
    jstring result = (*env)->NewStringUTF(env, hint);
alexey@8896
   404
    (*env)->ReleaseStringUTFChars(env, name, utfname);
alexey@8896
   405
alexey@8896
   406
    return result;
alexey@8896
   407
}
slouken@6555
   408
slouken@4964
   409
/*******************************************************************************
slouken@4964
   410
             Functions called by SDL into Java
slouken@4964
   411
*******************************************************************************/
slouken@6284
   412
ewing@7501
   413
static int s_active = 0;
ewing@7501
   414
struct LocalReferenceHolder
slouken@6284
   415
{
slouken@6284
   416
    JNIEnv *m_env;
slouken@6650
   417
    const char *m_func;
slouken@6284
   418
};
slouken@6284
   419
ewing@7501
   420
static struct LocalReferenceHolder LocalReferenceHolder_Setup(const char *func)
ewing@7501
   421
{
ewing@7501
   422
    struct LocalReferenceHolder refholder;
ewing@7501
   423
    refholder.m_env = NULL;
ewing@7501
   424
    refholder.m_func = func;
ewing@7501
   425
#ifdef DEBUG_JNI
ewing@7501
   426
    SDL_Log("Entering function %s", func);
ewing@7501
   427
#endif
ewing@7501
   428
    return refholder;
ewing@7501
   429
}
ewing@7501
   430
ewing@7501
   431
static SDL_bool LocalReferenceHolder_Init(struct LocalReferenceHolder *refholder, JNIEnv *env)
ewing@7501
   432
{
ewing@7501
   433
    const int capacity = 16;
ewing@7501
   434
    if ((*env)->PushLocalFrame(env, capacity) < 0) {
ewing@7501
   435
        SDL_SetError("Failed to allocate enough JVM local references");
philipp@7516
   436
        return SDL_FALSE;
ewing@7501
   437
    }
ewing@7501
   438
    ++s_active;
ewing@7501
   439
    refholder->m_env = env;
ewing@7501
   440
    return SDL_TRUE;
ewing@7501
   441
}
ewing@7501
   442
ewing@7501
   443
static void LocalReferenceHolder_Cleanup(struct LocalReferenceHolder *refholder)
ewing@7501
   444
{
ewing@7501
   445
#ifdef DEBUG_JNI
ewing@7501
   446
    SDL_Log("Leaving function %s", refholder->m_func);
ewing@7501
   447
#endif
ewing@7501
   448
    if (refholder->m_env) {
ewing@7501
   449
        JNIEnv* env = refholder->m_env;
ewing@7501
   450
        (*env)->PopLocalFrame(env, NULL);
ewing@7501
   451
        --s_active;
ewing@7501
   452
    }
ewing@7501
   453
}
ewing@7501
   454
philipp@9594
   455
static SDL_bool LocalReferenceHolder_IsActive(void)
ewing@7501
   456
{
gabomdq@9173
   457
    return s_active > 0;
gabomdq@9173
   458
}
gabomdq@9173
   459
gabomdq@7659
   460
ANativeWindow* Android_JNI_GetNativeWindow(void)
slouken@4964
   461
{
gabomdq@7659
   462
    ANativeWindow* anw;
gabomdq@7659
   463
    jobject s;
slouken@6792
   464
    JNIEnv *env = Android_JNI_GetEnv();
slouken@6792
   465
gabomdq@7659
   466
    s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface);
gabomdq@7659
   467
    anw = ANativeWindow_fromSurface(env, s);
gabomdq@7659
   468
    (*env)->DeleteLocalRef(env, s);
gabomdq@7659
   469
  
gabomdq@7659
   470
    return anw;
gabomdq@7567
   471
}
gabomdq@7567
   472
ewing@7501
   473
void Android_JNI_SetActivityTitle(const char *title)
slouken@4998
   474
{
slouken@4998
   475
    jmethodID mid;
gabomdq@6354
   476
    JNIEnv *mEnv = Android_JNI_GetEnv();
ewing@7501
   477
    mid = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,"setActivityTitle","(Ljava/lang/String;)Z");
slouken@4998
   478
    if (mid) {
ewing@7501
   479
        jstring jtitle = (jstring)((*mEnv)->NewStringUTF(mEnv, title));
ewing@7501
   480
        (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, mid, jtitle);
ewing@7501
   481
        (*mEnv)->DeleteLocalRef(mEnv, jtitle);
slouken@4998
   482
    }
slouken@4964
   483
}
slouken@4964
   484
ewing@7501
   485
SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
slouken@5000
   486
{
slouken@5000
   487
    int i;
slouken@6212
   488
    SDL_bool retval = SDL_FALSE;
slouken@6212
   489
slouken@6212
   490
    if (bHasNewData) {
slouken@6212
   491
        for (i = 0; i < 3; ++i) {
slouken@6212
   492
            values[i] = fLastAccelerometer[i];
slouken@6212
   493
        }
philipp@9271
   494
        bHasNewData = SDL_FALSE;
slouken@6212
   495
        retval = SDL_TRUE;
slouken@5000
   496
    }
slouken@6212
   497
slouken@6212
   498
    return retval;
slouken@5000
   499
}
slouken@5000
   500
slouken@8055
   501
static void Android_JNI_ThreadDestroyed(void* value)
slouken@8055
   502
{
gabomdq@6354
   503
    /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
gabomdq@6354
   504
    JNIEnv *env = (JNIEnv*) value;
gabomdq@6354
   505
    if (env != NULL) {
ewing@7501
   506
        (*mJavaVM)->DetachCurrentThread(mJavaVM);
gabomdq@6354
   507
        pthread_setspecific(mThreadKey, NULL);
gabomdq@6354
   508
    }
gabomdq@6354
   509
}
gabomdq@6354
   510
slouken@8055
   511
JNIEnv* Android_JNI_GetEnv(void)
slouken@8055
   512
{
gabomdq@6354
   513
    /* From http://developer.android.com/guide/practices/jni.html
gabomdq@6354
   514
     * All threads are Linux threads, scheduled by the kernel.
gabomdq@6354
   515
     * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then
gabomdq@6354
   516
     * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the
gabomdq@6354
   517
     * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv,
gabomdq@6354
   518
     * and cannot make JNI calls.
gabomdq@6354
   519
     * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main"
gabomdq@6354
   520
     * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread
gabomdq@6354
   521
     * is a no-op.
gabomdq@6354
   522
     * Note: You can call this function any number of times for the same thread, there's no harm in it
gabomdq@6354
   523
     */
gabomdq@6354
   524
gabomdq@6354
   525
    JNIEnv *env;
ewing@7501
   526
    int status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);
gabomdq@6354
   527
    if(status < 0) {
gabomdq@6354
   528
        LOGE("failed to attach current thread");
gabomdq@6354
   529
        return 0;
gabomdq@6354
   530
    }
gabomdq@6354
   531
gabomdq@6354
   532
    /* From http://developer.android.com/guide/practices/jni.html
gabomdq@6354
   533
     * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward,
gabomdq@6354
   534
     * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be
gabomdq@6354
   535
     * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific
gabomdq@6354
   536
     * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.)
gabomdq@6354
   537
     * Note: The destructor is not called unless the stored value is != NULL
gabomdq@6354
   538
     * Note: You can call this function any number of times for the same thread, there's no harm in it
gabomdq@6354
   539
     *       (except for some lost CPU cycles)
gabomdq@6354
   540
     */
gabomdq@6354
   541
    pthread_setspecific(mThreadKey, (void*) env);
slouken@8055
   542
slouken@8055
   543
    return env;
slouken@8055
   544
}
slouken@8055
   545
slouken@8055
   546
int Android_JNI_SetupThread(void)
slouken@8055
   547
{
slouken@8055
   548
    Android_JNI_GetEnv();
gabomdq@6354
   549
    return 1;
gabomdq@6354
   550
}
gabomdq@6354
   551
gabomdq@7663
   552
/*
gabomdq@7663
   553
 * Audio support
gabomdq@7663
   554
 */
slouken@4996
   555
static jboolean audioBuffer16Bit = JNI_FALSE;
slouken@4996
   556
static jobject audioBuffer = NULL;
slouken@4996
   557
static void* audioBufferPinned = NULL;
slouken@4995
   558
ewing@7501
   559
int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
slouken@4964
   560
{
philipp@9322
   561
    jboolean audioBufferStereo;
slouken@4996
   562
    int audioBufferFrames;
slouken@4964
   563
gabomdq@6354
   564
    JNIEnv *env = Android_JNI_GetEnv();
gabomdq@6354
   565
gabomdq@6354
   566
    if (!env) {
gabomdq@6354
   567
        LOGE("callback_handler: failed to attach current thread");
icculus@5996
   568
    }
gabomdq@6354
   569
    Android_JNI_SetupThread();
icculus@5996
   570
slouken@4996
   571
    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
slouken@4996
   572
    audioBuffer16Bit = is16Bit;
slouken@4996
   573
    audioBufferStereo = channelCount > 1;
slouken@4964
   574
gabomdq@7552
   575
    if ((*env)->CallStaticIntMethod(env, mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
gabomdq@7552
   576
        /* Error during audio initialization */
gabomdq@7552
   577
        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioTrack initialization!");
gabomdq@7552
   578
        return 0;
gabomdq@7552
   579
    }
gabomdq@6802
   580
gabomdq@6802
   581
    /* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on
gabomdq@6802
   582
     * Android >= 4.2 due to a "stale global reference" error. So now we allocate this buffer directly from this side. */
slouken@7191
   583
gabomdq@6802
   584
    if (is16Bit) {
ewing@7501
   585
        jshortArray audioBufferLocal = (*env)->NewShortArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
gabomdq@6802
   586
        if (audioBufferLocal) {
ewing@7501
   587
            audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
ewing@7501
   588
            (*env)->DeleteLocalRef(env, audioBufferLocal);
gabomdq@6802
   589
        }
gabomdq@6802
   590
    }
gabomdq@6802
   591
    else {
ewing@7501
   592
        jbyteArray audioBufferLocal = (*env)->NewByteArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
gabomdq@6802
   593
        if (audioBufferLocal) {
ewing@7501
   594
            audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
ewing@7501
   595
            (*env)->DeleteLocalRef(env, audioBufferLocal);
gabomdq@6802
   596
        }
gabomdq@6802
   597
    }
slouken@4995
   598
slouken@4996
   599
    if (audioBuffer == NULL) {
gabomdq@6802
   600
        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer!");
slouken@4996
   601
        return 0;
slouken@4996
   602
    }
slouken@4995
   603
slouken@4996
   604
    jboolean isCopy = JNI_FALSE;
slouken@4996
   605
    if (audioBuffer16Bit) {
ewing@7501
   606
        audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
ewing@7501
   607
        audioBufferFrames = (*env)->GetArrayLength(env, (jshortArray)audioBuffer);
slouken@4996
   608
    } else {
ewing@7501
   609
        audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
ewing@7501
   610
        audioBufferFrames = (*env)->GetArrayLength(env, (jbyteArray)audioBuffer);
slouken@4996
   611
    }
slouken@4996
   612
    if (audioBufferStereo) {
slouken@4996
   613
        audioBufferFrames /= 2;
slouken@4996
   614
    }
gabomdq@6864
   615
slouken@4996
   616
    return audioBufferFrames;
slouken@4995
   617
}
slouken@4995
   618
philipp@9594
   619
void * Android_JNI_GetAudioBuffer(void)
slouken@4995
   620
{
slouken@4996
   621
    return audioBufferPinned;
slouken@4995
   622
}
slouken@4995
   623
philipp@9594
   624
void Android_JNI_WriteAudioBuffer(void)
slouken@4995
   625
{
gabomdq@6354
   626
    JNIEnv *mAudioEnv = Android_JNI_GetEnv();
gabomdq@6354
   627
slouken@4996
   628
    if (audioBuffer16Bit) {
ewing@7501
   629
        (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
ewing@7501
   630
        (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
slouken@4996
   631
    } else {
ewing@7501
   632
        (*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
ewing@7501
   633
        (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
slouken@4996
   634
    }
slouken@4995
   635
slouken@4996
   636
    /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
slouken@4995
   637
}
slouken@4995
   638
philipp@9594
   639
void Android_JNI_CloseAudioDevice(void)
slouken@4995
   640
{
gabomdq@6354
   641
    JNIEnv *env = Android_JNI_GetEnv();
icculus@5996
   642
ewing@7501
   643
    (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioQuit);
slouken@4964
   644
slouken@4997
   645
    if (audioBuffer) {
ewing@7501
   646
        (*env)->DeleteGlobalRef(env, audioBuffer);
slouken@4997
   647
        audioBuffer = NULL;
slouken@4997
   648
        audioBufferPinned = NULL;
slouken@4997
   649
    }
slouken@4964
   650
}
slouken@4981
   651
gabomdq@7663
   652
/* Test for an exception and call SDL_SetError with its detail if one occurs */
gabomdq@7663
   653
/* If the parameter silent is truthy then SDL_SetError() will not be called. */
philipp@9271
   654
static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)
tim@5650
   655
{
ewing@7501
   656
    SDL_assert(LocalReferenceHolder_IsActive());
gabomdq@6354
   657
    JNIEnv *mEnv = Android_JNI_GetEnv();
slouken@6284
   658
ewing@7501
   659
    jthrowable exception = (*mEnv)->ExceptionOccurred(mEnv);
tim@5650
   660
    if (exception != NULL) {
tim@5650
   661
        jmethodID mid;
tim@5650
   662
gabomdq@7663
   663
        /* Until this happens most JNI operations have undefined behaviour */
ewing@7501
   664
        (*mEnv)->ExceptionClear(mEnv);
tim@5650
   665
slouken@7045
   666
        if (!silent) {
ewing@7501
   667
            jclass exceptionClass = (*mEnv)->GetObjectClass(mEnv, exception);
ewing@7501
   668
            jclass classClass = (*mEnv)->FindClass(mEnv, "java/lang/Class");
tim@5650
   669
ewing@7501
   670
            mid = (*mEnv)->GetMethodID(mEnv, classClass, "getName", "()Ljava/lang/String;");
ewing@7501
   671
            jstring exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid);
ewing@7501
   672
            const char* exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0);
tim@5650
   673
ewing@7501
   674
            mid = (*mEnv)->GetMethodID(mEnv, exceptionClass, "getMessage", "()Ljava/lang/String;");
ewing@7501
   675
            jstring exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid);
tim@5650
   676
slouken@7045
   677
            if (exceptionMessage != NULL) {
ewing@7501
   678
                const char* exceptionMessageUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionMessage, 0);
slouken@7045
   679
                SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
ewing@7501
   680
                (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionMessage, exceptionMessageUTF8);
slouken@7045
   681
            } else {
slouken@7045
   682
                SDL_SetError("%s", exceptionNameUTF8);
slouken@7045
   683
            }
slouken@7045
   684
ewing@7501
   685
            (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionName, exceptionNameUTF8);
tim@5650
   686
        }
tim@5650
   687
philipp@9271
   688
        return SDL_TRUE;
tim@5650
   689
    }
tim@5650
   690
philipp@9271
   691
    return SDL_FALSE;
tim@5650
   692
}
tim@5650
   693
ewing@7501
   694
static int Internal_Android_JNI_FileOpen(SDL_RWops* ctx)
icculus@5582
   695
{
ewing@7501
   696
    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
ewing@7501
   697
tim@5650
   698
    int result = 0;
tim@5650
   699
tim@5650
   700
    jmethodID mid;
tim@5650
   701
    jobject context;
tim@5650
   702
    jobject assetManager;
tim@5650
   703
    jobject inputStream;
tim@5650
   704
    jclass channels;
tim@5650
   705
    jobject readableByteChannel;
tim@5650
   706
    jstring fileNameJString;
gabomdq@6806
   707
    jobject fd;
gabomdq@6806
   708
    jclass fdCls;
gabomdq@6806
   709
    jfieldID descriptor;
tim@5650
   710
gabomdq@6354
   711
    JNIEnv *mEnv = Android_JNI_GetEnv();
ewing@7501
   712
    if (!LocalReferenceHolder_Init(&refs, mEnv)) {
tim@5650
   713
        goto failure;
tim@5650
   714
    }
tim@5650
   715
gabomdq@6308
   716
    fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef;
gabomdq@6806
   717
    ctx->hidden.androidio.position = 0;
icculus@5582
   718
gabomdq@7663
   719
    /* context = SDLActivity.getContext(); */
ewing@7501
   720
    mid = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
icculus@5582
   721
            "getContext","()Landroid/content/Context;");
ewing@7501
   722
    context = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, mid);
slouken@7191
   723
icculus@5582
   724
gabomdq@7663
   725
    /* assetManager = context.getAssets(); */
ewing@7501
   726
    mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
tim@5650
   727
            "getAssets", "()Landroid/content/res/AssetManager;");
ewing@7501
   728
    assetManager = (*mEnv)->CallObjectMethod(mEnv, context, mid);
icculus@5582
   729
gabomdq@6806
   730
    /* First let's try opening the file to obtain an AssetFileDescriptor.
gabomdq@6806
   731
    * This method reads the files directly from the APKs using standard *nix calls
gabomdq@6806
   732
    */
ewing@7501
   733
    mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
ewing@7501
   734
    inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString);
philipp@9271
   735
    if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
gabomdq@6806
   736
        goto fallback;
icculus@5582
   737
    }
icculus@5582
   738
ewing@7501
   739
    mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getStartOffset", "()J");
ewing@7501
   740
    ctx->hidden.androidio.offset = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
philipp@9271
   741
    if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
gabomdq@6806
   742
        goto fallback;
icculus@5582
   743
    }
icculus@5582
   744
ewing@7501
   745
    mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getDeclaredLength", "()J");
ewing@7501
   746
    ctx->hidden.androidio.size = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
philipp@9271
   747
    if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
gabomdq@6806
   748
        goto fallback;
icculus@5582
   749
    }
icculus@5582
   750
ewing@7501
   751
    mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;");
ewing@7501
   752
    fd = (*mEnv)->CallObjectMethod(mEnv, inputStream, mid);
ewing@7501
   753
    fdCls = (*mEnv)->GetObjectClass(mEnv, fd);
ewing@7501
   754
    descriptor = (*mEnv)->GetFieldID(mEnv, fdCls, "descriptor", "I");
ewing@7501
   755
    ctx->hidden.androidio.fd = (*mEnv)->GetIntField(mEnv, fd, descriptor);
ewing@7501
   756
    ctx->hidden.androidio.assetFileDescriptorRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
tim@5650
   757
gabomdq@7663
   758
    /* Seek to the correct offset in the file. */
gabomdq@6816
   759
    lseek(ctx->hidden.androidio.fd, (off_t)ctx->hidden.androidio.offset, SEEK_SET);
gabomdq@6816
   760
philipp@9271
   761
    if (0) {
gabomdq@6806
   762
fallback:
gabomdq@7663
   763
        /* Disabled log message because of spam on the Nexus 7 */
gabomdq@7678
   764
        /* __android_log_print(ANDROID_LOG_DEBUG, "SDL", "Falling back to legacy InputStream method for opening file"); */
slouken@7190
   765
gabomdq@6806
   766
        /* Try the old method using InputStream */
gabomdq@6806
   767
        ctx->hidden.androidio.assetFileDescriptorRef = NULL;
icculus@5582
   768
gabomdq@7663
   769
        /* inputStream = assetManager.open(<filename>); */
ewing@7501
   770
        mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager),
gabomdq@6806
   771
                "open", "(Ljava/lang/String;I)Ljava/io/InputStream;");
gabomdq@7678
   772
        inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */);
philipp@9271
   773
        if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
philipp@9869
   774
            /* Try fallback to APK expansion files */
alexey@8896
   775
            mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
philipp@9869
   776
                "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
philipp@9873
   777
            if (!mid) {
philipp@9873
   778
                SDL_SetError("No openAPKExpansionInputStream() in Java class");
philipp@9873
   779
                goto failure; /* Java class is missing the required method */
philipp@9873
   780
            }
alexey@8896
   781
            inputStream = (*mEnv)->CallObjectMethod(mEnv, context, mid, fileNameJString);
alexey@8896
   782
philipp@9870
   783
            /* Exception is checked first because it always needs to be cleared.
philipp@9870
   784
             * If no exception occurred then the last SDL error message is kept.
philipp@9870
   785
             */
philipp@9870
   786
            if (Android_JNI_ExceptionOccurred(SDL_FALSE) || !inputStream) {
alexey@8896
   787
                goto failure;
alexey@8896
   788
            }
gabomdq@6806
   789
        }
gabomdq@6806
   790
ewing@7501
   791
        ctx->hidden.androidio.inputStreamRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
gabomdq@6806
   792
gabomdq@7663
   793
        /* Despite all the visible documentation on [Asset]InputStream claiming
gabomdq@7663
   794
         * that the .available() method is not guaranteed to return the entire file
gabomdq@7663
   795
         * size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ...
gabomdq@7663
   796
         * android/apis/content/ReadAsset.java imply that Android's
gabomdq@7663
   797
         * AssetInputStream.available() /will/ always return the total file size
gabomdq@7663
   798
        */
gabomdq@7663
   799
        
gabomdq@7663
   800
        /* size = inputStream.available(); */
ewing@7501
   801
        mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
gabomdq@6806
   802
                "available", "()I");
ewing@7501
   803
        ctx->hidden.androidio.size = (long)(*mEnv)->CallIntMethod(mEnv, inputStream, mid);
philipp@9271
   804
        if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
gabomdq@6806
   805
            goto failure;
gabomdq@6806
   806
        }
gabomdq@6806
   807
gabomdq@7663
   808
        /* readableByteChannel = Channels.newChannel(inputStream); */
ewing@7501
   809
        channels = (*mEnv)->FindClass(mEnv, "java/nio/channels/Channels");
ewing@7501
   810
        mid = (*mEnv)->GetStaticMethodID(mEnv, channels,
gabomdq@6806
   811
                "newChannel",
gabomdq@6806
   812
                "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
ewing@7501
   813
        readableByteChannel = (*mEnv)->CallStaticObjectMethod(
ewing@7501
   814
                mEnv, channels, mid, inputStream);
philipp@9271
   815
        if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
gabomdq@6806
   816
            goto failure;
gabomdq@6806
   817
        }
gabomdq@6806
   818
gabomdq@6806
   819
        ctx->hidden.androidio.readableByteChannelRef =
ewing@7501
   820
            (*mEnv)->NewGlobalRef(mEnv, readableByteChannel);
gabomdq@6806
   821
gabomdq@7663
   822
        /* Store .read id for reading purposes */
ewing@7501
   823
        mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, readableByteChannel),
gabomdq@6806
   824
                "read", "(Ljava/nio/ByteBuffer;)I");
gabomdq@6806
   825
        ctx->hidden.androidio.readMethod = mid;
gabomdq@6806
   826
    }
icculus@5582
   827
philipp@9271
   828
    if (0) {
tim@5650
   829
failure:
tim@5650
   830
        result = -1;
tim@5650
   831
ewing@7501
   832
        (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef);
tim@5650
   833
tim@5650
   834
        if(ctx->hidden.androidio.inputStreamRef != NULL) {
ewing@7501
   835
            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef);
tim@5650
   836
        }
gabomdq@6308
   837
gabomdq@6308
   838
        if(ctx->hidden.androidio.readableByteChannelRef != NULL) {
ewing@7501
   839
            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef);
gabomdq@6308
   840
        }
gabomdq@6308
   841
gabomdq@6806
   842
        if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) {
ewing@7501
   843
            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
gabomdq@6806
   844
        }
gabomdq@6806
   845
tim@5650
   846
    }
ewing@7501
   847
    
ewing@7501
   848
    LocalReferenceHolder_Cleanup(&refs);
tim@5650
   849
    return result;
icculus@5582
   850
}
icculus@5582
   851
ewing@7501
   852
int Android_JNI_FileOpen(SDL_RWops* ctx,
ewing@7501
   853
        const char* fileName, const char* mode)
icculus@5582
   854
{
ewing@7501
   855
    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
gabomdq@6354
   856
    JNIEnv *mEnv = Android_JNI_GetEnv();
ewing@7501
   857
    int retval;
slouken@6284
   858
ewing@7501
   859
    if (!LocalReferenceHolder_Init(&refs, mEnv)) {
ewing@7501
   860
        LocalReferenceHolder_Cleanup(&refs);        
slouken@6284
   861
        return -1;
slouken@6284
   862
    }
slouken@6284
   863
icculus@5582
   864
    if (!ctx) {
ewing@7501
   865
        LocalReferenceHolder_Cleanup(&refs);
icculus@5582
   866
        return -1;
icculus@5582
   867
    }
icculus@5582
   868
ewing@7501
   869
    jstring fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName);
ewing@7501
   870
    ctx->hidden.androidio.fileNameRef = (*mEnv)->NewGlobalRef(mEnv, fileNameJString);
tim@5650
   871
    ctx->hidden.androidio.inputStreamRef = NULL;
gabomdq@6335
   872
    ctx->hidden.androidio.readableByteChannelRef = NULL;
gabomdq@6335
   873
    ctx->hidden.androidio.readMethod = NULL;
gabomdq@6806
   874
    ctx->hidden.androidio.assetFileDescriptorRef = NULL;
icculus@5582
   875
ewing@7501
   876
    retval = Internal_Android_JNI_FileOpen(ctx);
ewing@7501
   877
    LocalReferenceHolder_Cleanup(&refs);
ewing@7501
   878
    return retval;
icculus@5582
   879
}
icculus@5582
   880
ewing@7501
   881
size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
icculus@5582
   882
        size_t size, size_t maxnum)
icculus@5582
   883
{
ewing@7501
   884
    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
icculus@5582
   885
gabomdq@6806
   886
    if (ctx->hidden.androidio.assetFileDescriptorRef) {
gabomdq@6806
   887
        size_t bytesMax = size * maxnum;
gabomdq@7678
   888
        if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) {
gabomdq@6806
   889
            bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position;
gabomdq@6806
   890
        }
gabomdq@6806
   891
        size_t result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
gabomdq@6806
   892
        if (result > 0) {
gabomdq@6806
   893
            ctx->hidden.androidio.position += result;
ewing@7501
   894
            LocalReferenceHolder_Cleanup(&refs);
gabomdq@6806
   895
            return result / size;
gabomdq@6806
   896
        }
ewing@7501
   897
        LocalReferenceHolder_Cleanup(&refs);
gabomdq@6806
   898
        return 0;
gabomdq@6806
   899
    } else {
gabomdq@6806
   900
        jlong bytesRemaining = (jlong) (size * maxnum);
gabomdq@6806
   901
        jlong bytesMax = (jlong) (ctx->hidden.androidio.size -  ctx->hidden.androidio.position);
gabomdq@6806
   902
        int bytesRead = 0;
gabomdq@6377
   903
gabomdq@6806
   904
        /* Don't read more bytes than those that remain in the file, otherwise we get an exception */
gabomdq@6806
   905
        if (bytesRemaining >  bytesMax) bytesRemaining = bytesMax;
slouken@6284
   906
gabomdq@6806
   907
        JNIEnv *mEnv = Android_JNI_GetEnv();
ewing@7501
   908
        if (!LocalReferenceHolder_Init(&refs, mEnv)) {
ewing@7501
   909
            LocalReferenceHolder_Cleanup(&refs);            
philipp@7368
   910
            return 0;
icculus@5582
   911
        }
icculus@5582
   912
gabomdq@6806
   913
        jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
gabomdq@6806
   914
        jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
ewing@7501
   915
        jobject byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv, buffer, bytesRemaining);
gabomdq@6806
   916
gabomdq@6806
   917
        while (bytesRemaining > 0) {
gabomdq@7663
   918
            /* result = readableByteChannel.read(...); */
ewing@7501
   919
            int result = (*mEnv)->CallIntMethod(mEnv, readableByteChannel, readMethod, byteBuffer);
gabomdq@6806
   920
philipp@9271
   921
            if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
ewing@7501
   922
                LocalReferenceHolder_Cleanup(&refs);            
gabomdq@6806
   923
                return 0;
gabomdq@6806
   924
            }
gabomdq@6806
   925
gabomdq@6806
   926
            if (result < 0) {
gabomdq@6806
   927
                break;
gabomdq@6806
   928
            }
gabomdq@6806
   929
gabomdq@6806
   930
            bytesRemaining -= result;
gabomdq@6806
   931
            bytesRead += result;
gabomdq@6806
   932
            ctx->hidden.androidio.position += result;
icculus@5582
   933
        }
ewing@7501
   934
        LocalReferenceHolder_Cleanup(&refs);                    
gabomdq@6806
   935
        return bytesRead / size;
slouken@7191
   936
    }
icculus@5582
   937
}
icculus@5582
   938
ewing@7501
   939
size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer,
icculus@5582
   940
        size_t size, size_t num)
icculus@5582
   941
{
icculus@5582
   942
    SDL_SetError("Cannot write to Android package filesystem");
icculus@5582
   943
    return 0;
icculus@5582
   944
}
icculus@5582
   945
philipp@9271
   946
static int Internal_Android_JNI_FileClose(SDL_RWops* ctx, SDL_bool release)
icculus@5582
   947
{
ewing@7501
   948
    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
ewing@7501
   949
icculus@5582
   950
    int result = 0;
gabomdq@6354
   951
    JNIEnv *mEnv = Android_JNI_GetEnv();
icculus@5582
   952
ewing@7501
   953
    if (!LocalReferenceHolder_Init(&refs, mEnv)) {
ewing@7501
   954
        LocalReferenceHolder_Cleanup(&refs);
icculus@7037
   955
        return SDL_SetError("Failed to allocate enough JVM local references");
slouken@6284
   956
    }
slouken@6284
   957
icculus@5582
   958
    if (ctx) {
icculus@5582
   959
        if (release) {
ewing@7501
   960
            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef);
icculus@5582
   961
        }
icculus@5582
   962
gabomdq@6806
   963
        if (ctx->hidden.androidio.assetFileDescriptorRef) {
gabomdq@6806
   964
            jobject inputStream = (jobject)ctx->hidden.androidio.assetFileDescriptorRef;
ewing@7501
   965
            jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
gabomdq@6806
   966
                    "close", "()V");
ewing@7501
   967
            (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
ewing@7501
   968
            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
philipp@9271
   969
            if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
gabomdq@6806
   970
                result = -1;
gabomdq@6806
   971
            }
gabomdq@6806
   972
        }
gabomdq@6806
   973
        else {
gabomdq@6806
   974
            jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef;
icculus@5582
   975
gabomdq@7677
   976
            /* inputStream.close(); */
ewing@7501
   977
            jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
gabomdq@6806
   978
                    "close", "()V");
ewing@7501
   979
            (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
ewing@7501
   980
            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef);
ewing@7501
   981
            (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef);
philipp@9271
   982
            if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
gabomdq@6806
   983
                result = -1;
gabomdq@6806
   984
            }
icculus@5582
   985
        }
icculus@5582
   986
icculus@5582
   987
        if (release) {
icculus@5582
   988
            SDL_FreeRW(ctx);
icculus@5582
   989
        }
icculus@5582
   990
    }
icculus@5582
   991
ewing@7501
   992
    LocalReferenceHolder_Cleanup(&refs);
icculus@5582
   993
    return result;
icculus@5582
   994
}
icculus@5582
   995
icculus@5582
   996
ewing@7501
   997
Sint64 Android_JNI_FileSize(SDL_RWops* ctx)
icculus@5582
   998
{
slouken@6642
   999
    return ctx->hidden.androidio.size;
slouken@6642
  1000
}
slouken@6642
  1001
ewing@7501
  1002
Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
slouken@6642
  1003
{
gabomdq@6806
  1004
    if (ctx->hidden.androidio.assetFileDescriptorRef) {
gabomdq@6806
  1005
        switch (whence) {
gabomdq@6806
  1006
            case RW_SEEK_SET:
gabomdq@7678
  1007
                if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
gabomdq@6806
  1008
                offset += ctx->hidden.androidio.offset;
gabomdq@6806
  1009
                break;
gabomdq@6806
  1010
            case RW_SEEK_CUR:
gabomdq@6806
  1011
                offset += ctx->hidden.androidio.position;
gabomdq@7678
  1012
                if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
gabomdq@6806
  1013
                offset += ctx->hidden.androidio.offset;
gabomdq@6806
  1014
                break;
gabomdq@6806
  1015
            case RW_SEEK_END:
gabomdq@6806
  1016
                offset = ctx->hidden.androidio.offset + ctx->hidden.androidio.size + offset;
gabomdq@6806
  1017
                break;
gabomdq@6806
  1018
            default:
icculus@7037
  1019
                return SDL_SetError("Unknown value for 'whence'");
gabomdq@6806
  1020
        }
gabomdq@6806
  1021
        whence = SEEK_SET;
icculus@5582
  1022
gabomdq@6806
  1023
        off_t ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
gabomdq@6806
  1024
        if (ret == -1) return -1;
gabomdq@6806
  1025
        ctx->hidden.androidio.position = ret - ctx->hidden.androidio.offset;
gabomdq@6806
  1026
    } else {
gabomdq@6806
  1027
        Sint64 newPosition;
gabomdq@6806
  1028
gabomdq@6806
  1029
        switch (whence) {
gabomdq@6806
  1030
            case RW_SEEK_SET:
gabomdq@6806
  1031
                newPosition = offset;
gabomdq@6806
  1032
                break;
gabomdq@6806
  1033
            case RW_SEEK_CUR:
gabomdq@6806
  1034
                newPosition = ctx->hidden.androidio.position + offset;
gabomdq@6806
  1035
                break;
gabomdq@6806
  1036
            case RW_SEEK_END:
gabomdq@6806
  1037
                newPosition = ctx->hidden.androidio.size + offset;
gabomdq@6806
  1038
                break;
gabomdq@6806
  1039
            default:
icculus@7037
  1040
                return SDL_SetError("Unknown value for 'whence'");
gabomdq@6806
  1041
        }
gabomdq@6806
  1042
gabomdq@6806
  1043
        /* Validate the new position */
gabomdq@6806
  1044
        if (newPosition < 0) {
icculus@7037
  1045
            return SDL_Error(SDL_EFSEEK);
gabomdq@6806
  1046
        }
gabomdq@6806
  1047
        if (newPosition > ctx->hidden.androidio.size) {
gabomdq@6806
  1048
            newPosition = ctx->hidden.androidio.size;
gabomdq@6806
  1049
        }
slouken@6642
  1050
gabomdq@6806
  1051
        Sint64 movement = newPosition - ctx->hidden.androidio.position;
gabomdq@6806
  1052
        if (movement > 0) {
gabomdq@6806
  1053
            unsigned char buffer[4096];
icculus@5582
  1054
gabomdq@7663
  1055
            /* The easy case where we're seeking forwards */
gabomdq@6806
  1056
            while (movement > 0) {
gabomdq@6806
  1057
                Sint64 amount = sizeof (buffer);
gabomdq@6806
  1058
                if (amount > movement) {
gabomdq@6806
  1059
                    amount = movement;
gabomdq@6806
  1060
                }
gabomdq@6806
  1061
                size_t result = Android_JNI_FileRead(ctx, buffer, 1, amount);
gabomdq@6806
  1062
                if (result <= 0) {
gabomdq@7663
  1063
                    /* Failed to read/skip the required amount, so fail */
gabomdq@6806
  1064
                    return -1;
gabomdq@6806
  1065
                }
tim@5993
  1066
gabomdq@6806
  1067
                movement -= result;
icculus@5582
  1068
            }
tim@5993
  1069
gabomdq@6806
  1070
        } else if (movement < 0) {
gabomdq@7663
  1071
            /* We can't seek backwards so we have to reopen the file and seek */
gabomdq@7663
  1072
            /* forwards which obviously isn't very efficient */
philipp@9271
  1073
            Internal_Android_JNI_FileClose(ctx, SDL_FALSE);
ewing@7501
  1074
            Internal_Android_JNI_FileOpen(ctx);
gabomdq@6806
  1075
            Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
icculus@5582
  1076
        }
icculus@5582
  1077
    }
icculus@5582
  1078
icculus@5582
  1079
    return ctx->hidden.androidio.position;
slouken@7191
  1080
icculus@5582
  1081
}
icculus@5582
  1082
ewing@7501
  1083
int Android_JNI_FileClose(SDL_RWops* ctx)
icculus@5582
  1084
{
philipp@9271
  1085
    return Internal_Android_JNI_FileClose(ctx, SDL_TRUE);
icculus@5582
  1086
}
icculus@5582
  1087
gabomdq@7663
  1088
/* returns a new global reference which needs to be released later */
slouken@6464
  1089
static jobject Android_JNI_GetSystemServiceObject(const char* name)
slouken@6464
  1090
{
ewing@7501
  1091
    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
slouken@6464
  1092
    JNIEnv* env = Android_JNI_GetEnv();
ewing@7501
  1093
    jobject retval = NULL;
ewing@7501
  1094
ewing@7501
  1095
    if (!LocalReferenceHolder_Init(&refs, env)) {
ewing@7501
  1096
        LocalReferenceHolder_Cleanup(&refs);
slouken@6464
  1097
        return NULL;
slouken@6464
  1098
    }
slouken@6464
  1099
ewing@7501
  1100
    jstring service = (*env)->NewStringUTF(env, name);
slouken@6464
  1101
slouken@6464
  1102
    jmethodID mid;
slouken@6464
  1103
ewing@7501
  1104
    mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;");
ewing@7501
  1105
    jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
slouken@6464
  1106
philipp@8029
  1107
    mid = (*env)->GetMethodID(env, mActivityClass, "getSystemServiceFromUiThread", "(Ljava/lang/String;)Ljava/lang/Object;");
ewing@7501
  1108
    jobject manager = (*env)->CallObjectMethod(env, context, mid, service);
slouken@6464
  1109
ewing@7501
  1110
    (*env)->DeleteLocalRef(env, service);
slouken@6464
  1111
ewing@7501
  1112
    retval = manager ? (*env)->NewGlobalRef(env, manager) : NULL;
ewing@7501
  1113
    LocalReferenceHolder_Cleanup(&refs);
ewing@7501
  1114
    return retval;
slouken@6464
  1115
}
slouken@6464
  1116
slouken@6464
  1117
#define SETUP_CLIPBOARD(error) \
ewing@7501
  1118
    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); \
slouken@6464
  1119
    JNIEnv* env = Android_JNI_GetEnv(); \
ewing@7501
  1120
    if (!LocalReferenceHolder_Init(&refs, env)) { \
ewing@7501
  1121
        LocalReferenceHolder_Cleanup(&refs); \
slouken@6464
  1122
        return error; \
slouken@6464
  1123
    } \
slouken@6464
  1124
    jobject clipboard = Android_JNI_GetSystemServiceObject("clipboard"); \
slouken@6464
  1125
    if (!clipboard) { \
ewing@7501
  1126
        LocalReferenceHolder_Cleanup(&refs); \
slouken@6464
  1127
        return error; \
slouken@6464
  1128
    }
slouken@6464
  1129
ewing@7501
  1130
#define CLEANUP_CLIPBOARD() \
ewing@7501
  1131
    LocalReferenceHolder_Cleanup(&refs);
ewing@7501
  1132
ewing@7501
  1133
int Android_JNI_SetClipboardText(const char* text)
slouken@6464
  1134
{
slouken@6464
  1135
    SETUP_CLIPBOARD(-1)
slouken@6464
  1136
ewing@7501
  1137
    jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "setText", "(Ljava/lang/CharSequence;)V");
ewing@7501
  1138
    jstring string = (*env)->NewStringUTF(env, text);
ewing@7501
  1139
    (*env)->CallVoidMethod(env, clipboard, mid, string);
ewing@7501
  1140
    (*env)->DeleteGlobalRef(env, clipboard);
ewing@7501
  1141
    (*env)->DeleteLocalRef(env, string);
ewing@7501
  1142
ewing@7501
  1143
    CLEANUP_CLIPBOARD();
ewing@7501
  1144
slouken@6464
  1145
    return 0;
slouken@6464
  1146
}
slouken@6464
  1147
philipp@9594
  1148
char* Android_JNI_GetClipboardText(void)
slouken@6464
  1149
{
slouken@6464
  1150
    SETUP_CLIPBOARD(SDL_strdup(""))
slouken@6464
  1151
ewing@7501
  1152
    jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "getText", "()Ljava/lang/CharSequence;");
ewing@7501
  1153
    jobject sequence = (*env)->CallObjectMethod(env, clipboard, mid);
ewing@7501
  1154
    (*env)->DeleteGlobalRef(env, clipboard);
slouken@6464
  1155
    if (sequence) {
ewing@7501
  1156
        mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, sequence), "toString", "()Ljava/lang/String;");
ewing@7501
  1157
        jstring string = (jstring)((*env)->CallObjectMethod(env, sequence, mid));
ewing@7501
  1158
        const char* utf = (*env)->GetStringUTFChars(env, string, 0);
slouken@6464
  1159
        if (utf) {
slouken@6464
  1160
            char* text = SDL_strdup(utf);
ewing@7501
  1161
            (*env)->ReleaseStringUTFChars(env, string, utf);
ewing@7501
  1162
ewing@7501
  1163
            CLEANUP_CLIPBOARD();
ewing@7501
  1164
slouken@6464
  1165
            return text;
slouken@6464
  1166
        }
slouken@6464
  1167
    }
ewing@7501
  1168
ewing@7501
  1169
    CLEANUP_CLIPBOARD();    
ewing@7501
  1170
slouken@6464
  1171
    return SDL_strdup("");
slouken@6464
  1172
}
slouken@6464
  1173
philipp@9594
  1174
SDL_bool Android_JNI_HasClipboardText(void)
slouken@6464
  1175
{
slouken@6464
  1176
    SETUP_CLIPBOARD(SDL_FALSE)
slouken@6464
  1177
ewing@7501
  1178
    jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "hasText", "()Z");
ewing@7501
  1179
    jboolean has = (*env)->CallBooleanMethod(env, clipboard, mid);
ewing@7501
  1180
    (*env)->DeleteGlobalRef(env, clipboard);
ewing@7501
  1181
ewing@7501
  1182
    CLEANUP_CLIPBOARD();
ewing@7501
  1183
    
slouken@6464
  1184
    return has ? SDL_TRUE : SDL_FALSE;
slouken@6464
  1185
}
slouken@6464
  1186
slouken@6464
  1187
gabomdq@7663
  1188
/* returns 0 on success or -1 on error (others undefined then)
gabomdq@7663
  1189
 * returns truthy or falsy value in plugged, charged and battery
gabomdq@7663
  1190
 * returns the value in seconds and percent or -1 if not available
gabomdq@7663
  1191
 */
ewing@7501
  1192
int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent)
slouken@6448
  1193
{
ewing@7501
  1194
    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
slouken@6448
  1195
    JNIEnv* env = Android_JNI_GetEnv();
ewing@7501
  1196
    if (!LocalReferenceHolder_Init(&refs, env)) {
ewing@7501
  1197
        LocalReferenceHolder_Cleanup(&refs);
slouken@6448
  1198
        return -1;
slouken@6448
  1199
    }
slouken@6448
  1200
slouken@6448
  1201
    jmethodID mid;
slouken@6448
  1202
ewing@7501
  1203
    mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;");
ewing@7501
  1204
    jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
slouken@6448
  1205
ewing@7501
  1206
    jstring action = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED");
slouken@6448
  1207
ewing@7501
  1208
    jclass cls = (*env)->FindClass(env, "android/content/IntentFilter");
slouken@6448
  1209
ewing@7501
  1210
    mid = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
ewing@7501
  1211
    jobject filter = (*env)->NewObject(env, cls, mid, action);
slouken@6448
  1212
ewing@7501
  1213
    (*env)->DeleteLocalRef(env, action);
slouken@6448
  1214
ewing@7501
  1215
    mid = (*env)->GetMethodID(env, mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
ewing@7501
  1216
    jobject intent = (*env)->CallObjectMethod(env, context, mid, NULL, filter);
slouken@6448
  1217
ewing@7501
  1218
    (*env)->DeleteLocalRef(env, filter);
slouken@6448
  1219
ewing@7501
  1220
    cls = (*env)->GetObjectClass(env, intent);
slouken@6448
  1221
slouken@6448
  1222
    jstring iname;
ewing@7501
  1223
    jmethodID imid = (*env)->GetMethodID(env, cls, "getIntExtra", "(Ljava/lang/String;I)I");
slouken@6448
  1224
slouken@6448
  1225
#define GET_INT_EXTRA(var, key) \
ewing@7501
  1226
    iname = (*env)->NewStringUTF(env, key); \
ewing@7501
  1227
    int var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \
ewing@7501
  1228
    (*env)->DeleteLocalRef(env, iname);
slouken@6448
  1229
slouken@6448
  1230
    jstring bname;
ewing@7501
  1231
    jmethodID bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
slouken@6448
  1232
slouken@6448
  1233
#define GET_BOOL_EXTRA(var, key) \
ewing@7501
  1234
    bname = (*env)->NewStringUTF(env, key); \
ewing@7501
  1235
    int var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \
ewing@7501
  1236
    (*env)->DeleteLocalRef(env, bname);
slouken@6448
  1237
slouken@6448
  1238
    if (plugged) {
gabomdq@7663
  1239
        GET_INT_EXTRA(plug, "plugged") /* == BatteryManager.EXTRA_PLUGGED (API 5) */
slouken@6448
  1240
        if (plug == -1) {
ewing@7501
  1241
            LocalReferenceHolder_Cleanup(&refs);
slouken@6448
  1242
            return -1;
slouken@6448
  1243
        }
gabomdq@7663
  1244
        /* 1 == BatteryManager.BATTERY_PLUGGED_AC */
gabomdq@7663
  1245
        /* 2 == BatteryManager.BATTERY_PLUGGED_USB */
slouken@6448
  1246
        *plugged = (0 < plug) ? 1 : 0;
slouken@6448
  1247
    }
slouken@6448
  1248
slouken@6448
  1249
    if (charged) {
gabomdq@7663
  1250
        GET_INT_EXTRA(status, "status") /* == BatteryManager.EXTRA_STATUS (API 5) */
slouken@6448
  1251
        if (status == -1) {
ewing@7501
  1252
            LocalReferenceHolder_Cleanup(&refs);
slouken@6448
  1253
            return -1;
slouken@6448
  1254
        }
gabomdq@7663
  1255
        /* 5 == BatteryManager.BATTERY_STATUS_FULL */
slouken@6448
  1256
        *charged = (status == 5) ? 1 : 0;
slouken@6448
  1257
    }
slouken@6448
  1258
slouken@6448
  1259
    if (battery) {
gabomdq@7663
  1260
        GET_BOOL_EXTRA(present, "present") /* == BatteryManager.EXTRA_PRESENT (API 5) */
slouken@6448
  1261
        *battery = present ? 1 : 0;
slouken@6448
  1262
    }
slouken@6448
  1263
slouken@6448
  1264
    if (seconds) {
gabomdq@7663
  1265
        *seconds = -1; /* not possible */
slouken@6448
  1266
    }
slouken@6448
  1267
slouken@6448
  1268
    if (percent) {
gabomdq@7663
  1269
        GET_INT_EXTRA(level, "level") /* == BatteryManager.EXTRA_LEVEL (API 5) */
gabomdq@7663
  1270
        GET_INT_EXTRA(scale, "scale") /* == BatteryManager.EXTRA_SCALE (API 5) */
slouken@6448
  1271
        if ((level == -1) || (scale == -1)) {
ewing@7501
  1272
            LocalReferenceHolder_Cleanup(&refs);
slouken@6448
  1273
            return -1;
slouken@6448
  1274
        }
slouken@6448
  1275
        *percent = level * 100 / scale;
slouken@6448
  1276
    }
slouken@6448
  1277
ewing@7501
  1278
    (*env)->DeleteLocalRef(env, intent);
slouken@6448
  1279
ewing@7501
  1280
    LocalReferenceHolder_Cleanup(&refs);
slouken@6448
  1281
    return 0;
slouken@6448
  1282
}
slouken@6448
  1283
philipp@7786
  1284
/* returns number of found touch devices as return value and ids in parameter ids */
philipp@7786
  1285
int Android_JNI_GetTouchDeviceIds(int **ids) {
philipp@7786
  1286
    JNIEnv *env = Android_JNI_GetEnv();
philipp@7786
  1287
    jint sources = 4098; /* == InputDevice.SOURCE_TOUCHSCREEN */
philipp@7786
  1288
    jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "inputGetInputDeviceIds", "(I)[I");
philipp@7786
  1289
    jintArray array = (jintArray) (*env)->CallStaticObjectMethod(env, mActivityClass, mid, sources);
philipp@7786
  1290
    int number = 0;
philipp@7786
  1291
    *ids = NULL;
philipp@7786
  1292
    if (array) {
philipp@7786
  1293
        number = (int) (*env)->GetArrayLength(env, array);
philipp@7786
  1294
        if (0 < number) {
philipp@7786
  1295
            jint* elements = (*env)->GetIntArrayElements(env, array, NULL);
philipp@7786
  1296
            if (elements) {
philipp@7786
  1297
                int i;
slouken@7863
  1298
                *ids = SDL_malloc(number * sizeof (**ids));
philipp@7786
  1299
                for (i = 0; i < number; ++i) { /* not assuming sizeof (jint) == sizeof (int) */
slouken@7863
  1300
                    (*ids)[i] = elements[i];
philipp@7786
  1301
                }
philipp@7786
  1302
                (*env)->ReleaseIntArrayElements(env, array, elements, JNI_ABORT);
philipp@7786
  1303
            }
philipp@7786
  1304
        }
philipp@7786
  1305
        (*env)->DeleteLocalRef(env, array);
philipp@7786
  1306
    }
philipp@7786
  1307
    return number;
philipp@7786
  1308
}
philipp@7786
  1309
philipp@9594
  1310
void Android_JNI_PollInputDevices(void)
gabomdq@7907
  1311
{
gabomdq@8057
  1312
    JNIEnv *env = Android_JNI_GetEnv();
gabomdq@8057
  1313
    (*env)->CallStaticVoidMethod(env, mActivityClass, midPollInputDevices);    
gabomdq@7907
  1314
}
gabomdq@7907
  1315
philipp@9174
  1316
/* See SDLActivity.java for constants. */
philipp@9174
  1317
#define COMMAND_SET_KEEP_SCREEN_ON    5
philipp@9174
  1318
gabomdq@7663
  1319
/* sends message to be handled on the UI event dispatch thread */
ewing@7501
  1320
int Android_JNI_SendMessage(int command, int param)
slouken@6392
  1321
{
slouken@6392
  1322
    JNIEnv *env = Android_JNI_GetEnv();
slouken@6392
  1323
    if (!env) {
slouken@6392
  1324
        return -1;
slouken@6392
  1325
    }
ewing@7501
  1326
    jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z");
slouken@6392
  1327
    if (!mid) {
slouken@6392
  1328
        return -1;
slouken@6392
  1329
    }
ewing@7501
  1330
    jboolean success = (*env)->CallStaticBooleanMethod(env, mActivityClass, mid, command, param);
philipp@7149
  1331
    return success ? 0 : -1;
slouken@6392
  1332
}
slouken@6392
  1333
philipp@9174
  1334
void Android_JNI_SuspendScreenSaver(SDL_bool suspend)
philipp@9174
  1335
{
philipp@9174
  1336
    Android_JNI_SendMessage(COMMAND_SET_KEEP_SCREEN_ON, (suspend == SDL_FALSE) ? 0 : 1);
philipp@9174
  1337
}
philipp@9174
  1338
ewing@7501
  1339
void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
slouken@6555
  1340
{
slouken@6555
  1341
    JNIEnv *env = Android_JNI_GetEnv();
slouken@6555
  1342
    if (!env) {
slouken@6654
  1343
        return;
slouken@6555
  1344
    }
slouken@6555
  1345
ewing@7501
  1346
    jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIII)Z");
slouken@6555
  1347
    if (!mid) {
slouken@6654
  1348
        return;
slouken@6555
  1349
    }
ewing@7501
  1350
    (*env)->CallStaticBooleanMethod(env, mActivityClass, mid,
slouken@6555
  1351
                               inputRect->x,
slouken@6555
  1352
                               inputRect->y,
slouken@6555
  1353
                               inputRect->w,
slouken@6555
  1354
                               inputRect->h );
slouken@6555
  1355
}
slouken@6555
  1356
philipp@9594
  1357
void Android_JNI_HideTextInput(void)
slouken@6555
  1358
{
gabomdq@7663
  1359
    /* has to match Activity constant */
slouken@6654
  1360
    const int COMMAND_TEXTEDIT_HIDE = 3;
slouken@6654
  1361
    Android_JNI_SendMessage(COMMAND_TEXTEDIT_HIDE, 0);
slouken@6654
  1362
}
slouken@6555
  1363
slouken@9135
  1364
int Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
slouken@9135
  1365
{
slouken@9135
  1366
    JNIEnv *env;
philipp@9231
  1367
    jclass clazz;
slouken@9135
  1368
    jmethodID mid;
slouken@9135
  1369
    jobject context;
slouken@9135
  1370
    jstring title;
slouken@9135
  1371
    jstring message;
slouken@9135
  1372
    jintArray button_flags;
slouken@9135
  1373
    jintArray button_ids;
slouken@9135
  1374
    jobjectArray button_texts;
slouken@9135
  1375
    jintArray colors;
philipp@9231
  1376
    jobject text;
slouken@9135
  1377
    jint temp;
slouken@9135
  1378
    int i;
slouken@9135
  1379
slouken@9135
  1380
    env = Android_JNI_GetEnv();
slouken@9135
  1381
slouken@9135
  1382
    /* convert parameters */
slouken@9135
  1383
philipp@9231
  1384
    clazz = (*env)->FindClass(env, "java/lang/String");
philipp@9231
  1385
slouken@9135
  1386
    title = (*env)->NewStringUTF(env, messageboxdata->title);
slouken@9135
  1387
    message = (*env)->NewStringUTF(env, messageboxdata->message);
slouken@9135
  1388
slouken@9135
  1389
    button_flags = (*env)->NewIntArray(env, messageboxdata->numbuttons);
slouken@9135
  1390
    button_ids = (*env)->NewIntArray(env, messageboxdata->numbuttons);
slouken@9135
  1391
    button_texts = (*env)->NewObjectArray(env, messageboxdata->numbuttons,
philipp@9231
  1392
        clazz, NULL);
slouken@9135
  1393
    for (i = 0; i < messageboxdata->numbuttons; ++i) {
slouken@9135
  1394
        temp = messageboxdata->buttons[i].flags;
slouken@9135
  1395
        (*env)->SetIntArrayRegion(env, button_flags, i, 1, &temp);
slouken@9135
  1396
        temp = messageboxdata->buttons[i].buttonid;
slouken@9135
  1397
        (*env)->SetIntArrayRegion(env, button_ids, i, 1, &temp);
philipp@9231
  1398
        text = (*env)->NewStringUTF(env, messageboxdata->buttons[i].text);
philipp@9231
  1399
        (*env)->SetObjectArrayElement(env, button_texts, i, text);
philipp@9231
  1400
        (*env)->DeleteLocalRef(env, text);
slouken@9135
  1401
    }
slouken@9135
  1402
slouken@9135
  1403
    if (messageboxdata->colorScheme) {
slouken@9135
  1404
        colors = (*env)->NewIntArray(env, SDL_MESSAGEBOX_COLOR_MAX);
slouken@9135
  1405
        for (i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; ++i) {
slouken@9135
  1406
            temp = (0xFF << 24) |
slouken@9135
  1407
                   (messageboxdata->colorScheme->colors[i].r << 16) |
slouken@9135
  1408
                   (messageboxdata->colorScheme->colors[i].g << 8) |
slouken@9135
  1409
                   (messageboxdata->colorScheme->colors[i].b << 0);
slouken@9135
  1410
            (*env)->SetIntArrayRegion(env, colors, i, 1, &temp);
slouken@9135
  1411
        }
slouken@9135
  1412
    } else {
slouken@9135
  1413
        colors = NULL;
slouken@9135
  1414
    }
slouken@9135
  1415
philipp@9231
  1416
    (*env)->DeleteLocalRef(env, clazz);
philipp@9231
  1417
slouken@9135
  1418
    /* call function */
slouken@9135
  1419
slouken@9135
  1420
    mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext","()Landroid/content/Context;");
slouken@9135
  1421
slouken@9135
  1422
    context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
slouken@9135
  1423
philipp@9231
  1424
    clazz = (*env)->GetObjectClass(env, context);
philipp@9231
  1425
philipp@9231
  1426
    mid = (*env)->GetMethodID(env, clazz,
slouken@9135
  1427
        "messageboxShowMessageBox", "(ILjava/lang/String;Ljava/lang/String;[I[I[Ljava/lang/String;[I)I");
slouken@9135
  1428
    *buttonid = (*env)->CallIntMethod(env, context, mid,
slouken@9135
  1429
        messageboxdata->flags,
slouken@9135
  1430
        title,
slouken@9135
  1431
        message,
slouken@9135
  1432
        button_flags,
slouken@9135
  1433
        button_ids,
slouken@9135
  1434
        button_texts,
slouken@9135
  1435
        colors);
slouken@9135
  1436
philipp@9231
  1437
    (*env)->DeleteLocalRef(env, context);
philipp@9231
  1438
    (*env)->DeleteLocalRef(env, clazz);
philipp@9231
  1439
slouken@9135
  1440
    /* delete parameters */
slouken@9135
  1441
slouken@9135
  1442
    (*env)->DeleteLocalRef(env, title);
slouken@9135
  1443
    (*env)->DeleteLocalRef(env, message);
slouken@9135
  1444
    (*env)->DeleteLocalRef(env, button_flags);
slouken@9135
  1445
    (*env)->DeleteLocalRef(env, button_ids);
slouken@9135
  1446
    (*env)->DeleteLocalRef(env, button_texts);
slouken@9135
  1447
    (*env)->DeleteLocalRef(env, colors);
slouken@9135
  1448
slouken@9135
  1449
    return 0;
slouken@9135
  1450
}
slouken@9135
  1451
gabomdq@7663
  1452
/*
slouken@6630
  1453
//////////////////////////////////////////////////////////////////////////////
slouken@6630
  1454
//
slouken@6630
  1455
// Functions exposed to SDL applications in SDL_system.h
gabomdq@7663
  1456
//////////////////////////////////////////////////////////////////////////////
gabomdq@7663
  1457
*/
slouken@6630
  1458
ewing@7501
  1459
void *SDL_AndroidGetJNIEnv()
slouken@6630
  1460
{
slouken@6630
  1461
    return Android_JNI_GetEnv();
slouken@6630
  1462
}
slouken@6630
  1463
gabomdq@7095
  1464
gabomdq@7083
  1465
ewing@7501
  1466
void *SDL_AndroidGetActivity()
slouken@6630
  1467
{
gabomdq@7095
  1468
    /* See SDL_system.h for caveats on using this function. */
slouken@7191
  1469
slouken@6630
  1470
    jmethodID mid;
slouken@6630
  1471
slouken@6630
  1472
    JNIEnv *env = Android_JNI_GetEnv();
gabomdq@7083
  1473
    if (!env) {
slouken@6630
  1474
        return NULL;
slouken@6630
  1475
    }
slouken@6630
  1476
gabomdq@7663
  1477
    /* return SDLActivity.getContext(); */
ewing@7501
  1478
    mid = (*env)->GetStaticMethodID(env, mActivityClass,
slouken@6630
  1479
            "getContext","()Landroid/content/Context;");
ewing@7501
  1480
    return (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
slouken@6630
  1481
}
slouken@6630
  1482
ewing@7501
  1483
const char * SDL_AndroidGetInternalStoragePath()
slouken@6630
  1484
{
slouken@6630
  1485
    static char *s_AndroidInternalFilesPath = NULL;
slouken@6630
  1486
slouken@6630
  1487
    if (!s_AndroidInternalFilesPath) {
ewing@7501
  1488
        struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
slouken@6630
  1489
        jmethodID mid;
slouken@6630
  1490
        jobject context;
slouken@6630
  1491
        jobject fileObject;
slouken@6630
  1492
        jstring pathString;
slouken@6630
  1493
        const char *path;
slouken@6630
  1494
slouken@6630
  1495
        JNIEnv *env = Android_JNI_GetEnv();
ewing@7501
  1496
        if (!LocalReferenceHolder_Init(&refs, env)) {
ewing@7501
  1497
            LocalReferenceHolder_Cleanup(&refs);
slouken@6630
  1498
            return NULL;
slouken@6630
  1499
        }
slouken@6630
  1500
gabomdq@7663
  1501
        /* context = SDLActivity.getContext(); */
ewing@7501
  1502
        mid = (*env)->GetStaticMethodID(env, mActivityClass,
slouken@6630
  1503
                "getContext","()Landroid/content/Context;");
ewing@7501
  1504
        context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
slouken@6630
  1505
gabomdq@7663
  1506
        /* fileObj = context.getFilesDir(); */
ewing@7501
  1507
        mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
slouken@6630
  1508
                "getFilesDir", "()Ljava/io/File;");
ewing@7501
  1509
        fileObject = (*env)->CallObjectMethod(env, context, mid);
slouken@6630
  1510
        if (!fileObject) {
slouken@6630
  1511
            SDL_SetError("Couldn't get internal directory");
ewing@7501
  1512
            LocalReferenceHolder_Cleanup(&refs);
slouken@6630
  1513
            return NULL;
slouken@6630
  1514
        }
slouken@6630
  1515
gabomdq@7663
  1516
        /* path = fileObject.getAbsolutePath(); */
ewing@7501
  1517
        mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
slouken@6630
  1518
                "getAbsolutePath", "()Ljava/lang/String;");
ewing@7501
  1519
        pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
slouken@6630
  1520
ewing@7501
  1521
        path = (*env)->GetStringUTFChars(env, pathString, NULL);
slouken@6630
  1522
        s_AndroidInternalFilesPath = SDL_strdup(path);
ewing@7501
  1523
        (*env)->ReleaseStringUTFChars(env, pathString, path);
ewing@7501
  1524
ewing@7501
  1525
        LocalReferenceHolder_Cleanup(&refs);
slouken@6630
  1526
    }
slouken@6630
  1527
    return s_AndroidInternalFilesPath;
slouken@6630
  1528
}
slouken@6630
  1529
ewing@7501
  1530
int SDL_AndroidGetExternalStorageState()
slouken@6630
  1531
{
ewing@7501
  1532
    struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
slouken@6630
  1533
    jmethodID mid;
slouken@6630
  1534
    jclass cls;
slouken@6630
  1535
    jstring stateString;
slouken@6630
  1536
    const char *state;
slouken@6630
  1537
    int stateFlags;
slouken@6630
  1538
slouken@6630
  1539
    JNIEnv *env = Android_JNI_GetEnv();
ewing@7501
  1540
    if (!LocalReferenceHolder_Init(&refs, env)) {
ewing@7501
  1541
        LocalReferenceHolder_Cleanup(&refs);
slouken@6630
  1542
        return 0;
slouken@6630
  1543
    }
slouken@6630
  1544
ewing@7501
  1545
    cls = (*env)->FindClass(env, "android/os/Environment");
ewing@7501
  1546
    mid = (*env)->GetStaticMethodID(env, cls,
slouken@6630
  1547
            "getExternalStorageState", "()Ljava/lang/String;");
ewing@7501
  1548
    stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid);
slouken@6630
  1549
ewing@7501
  1550
    state = (*env)->GetStringUTFChars(env, stateString, NULL);
slouken@6630
  1551
gabomdq@7663
  1552
    /* Print an info message so people debugging know the storage state */
slouken@6630
  1553
    __android_log_print(ANDROID_LOG_INFO, "SDL", "external storage state: %s", state);
slouken@6630
  1554
slouken@6630
  1555
    if (SDL_strcmp(state, "mounted") == 0) {
slouken@6630
  1556
        stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ |
slouken@6630
  1557
                     SDL_ANDROID_EXTERNAL_STORAGE_WRITE;
slouken@6630
  1558
    } else if (SDL_strcmp(state, "mounted_ro") == 0) {
slouken@6630
  1559
        stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ;
slouken@6630
  1560
    } else {
slouken@6630
  1561
        stateFlags = 0;
slouken@6630
  1562
    }
ewing@7501
  1563
    (*env)->ReleaseStringUTFChars(env, stateString, state);
slouken@6630
  1564
ewing@7501
  1565
    LocalReferenceHolder_Cleanup(&refs);
slouken@6630
  1566
    return stateFlags;
slouken@6630
  1567
}
slouken@6630
  1568
ewing@7501
  1569
const char * SDL_AndroidGetExternalStoragePath()
slouken@6630
  1570
{
slouken@6630
  1571
    static char *s_AndroidExternalFilesPath = NULL;
slouken@6630
  1572
slouken@6630
  1573
    if (!s_AndroidExternalFilesPath) {
ewing@7501
  1574
        struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
slouken@6630
  1575
        jmethodID mid;
slouken@6630
  1576
        jobject context;
slouken@6630
  1577
        jobject fileObject;
slouken@6630
  1578
        jstring pathString;
slouken@6630
  1579
        const char *path;
slouken@6630
  1580
slouken@6630
  1581
        JNIEnv *env = Android_JNI_GetEnv();
ewing@7501
  1582
        if (!LocalReferenceHolder_Init(&refs, env)) {
ewing@7501
  1583
            LocalReferenceHolder_Cleanup(&refs);
slouken@6630
  1584
            return NULL;
slouken@6630
  1585
        }
slouken@6630
  1586
gabomdq@7663
  1587
        /* context = SDLActivity.getContext(); */
ewing@7501
  1588
        mid = (*env)->GetStaticMethodID(env, mActivityClass,
slouken@6630
  1589
                "getContext","()Landroid/content/Context;");
ewing@7501
  1590
        context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
slouken@6630
  1591
gabomdq@7663
  1592
        /* fileObj = context.getExternalFilesDir(); */
ewing@7501
  1593
        mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
slouken@6630
  1594
                "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
ewing@7501
  1595
        fileObject = (*env)->CallObjectMethod(env, context, mid, NULL);
slouken@6630
  1596
        if (!fileObject) {
slouken@6630
  1597
            SDL_SetError("Couldn't get external directory");
ewing@7501
  1598
            LocalReferenceHolder_Cleanup(&refs);
slouken@6630
  1599
            return NULL;
slouken@6630
  1600
        }
slouken@6630
  1601
gabomdq@7663
  1602
        /* path = fileObject.getAbsolutePath(); */
ewing@7501
  1603
        mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
slouken@6630
  1604
                "getAbsolutePath", "()Ljava/lang/String;");
ewing@7501
  1605
        pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
slouken@6630
  1606
ewing@7501
  1607
        path = (*env)->GetStringUTFChars(env, pathString, NULL);
slouken@6630
  1608
        s_AndroidExternalFilesPath = SDL_strdup(path);
ewing@7501
  1609
        (*env)->ReleaseStringUTFChars(env, pathString, path);
ewing@7501
  1610
ewing@7501
  1611
        LocalReferenceHolder_Cleanup(&refs);
slouken@6630
  1612
    }
slouken@6630
  1613
    return s_AndroidExternalFilesPath;
slouken@6630
  1614
}
slouken@6630
  1615
icculus@9450
  1616
jclass Android_JNI_GetActivityClass(void)
icculus@9450
  1617
{
icculus@9450
  1618
    return mActivityClass;
icculus@9450
  1619
}
icculus@9450
  1620
slouken@6044
  1621
#endif /* __ANDROID__ */
slouken@6044
  1622
slouken@4981
  1623
/* vi: set ts=4 sw=4 expandtab: */
gabomdq@7612
  1624