project_files/Android-build/SDL-android-project/jni/SDL/src/core/android/SDL_android.cpp
changeset 6592 cf12f07d6f24
parent 6328 d14adf1c7721
child 6597 814683bbd230
equal deleted inserted replaced
6591:2abf9ea19a38 6592:cf12f07d6f24
     1 /*
     1 /*
     2     SDL - Simple DirectMedia Layer
     2   Simple DirectMedia Layer
     3     Copyright (C) 1997-2011 Sam Lantinga
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     4 
     5     This library is free software; you can redistribute it and/or
     5   This software is provided 'as-is', without any express or implied
     6     modify it under the terms of the GNU Lesser General Public
     6   warranty.  In no event will the authors be held liable for any damages
     7     License as published by the Free Software Foundation; either
     7   arising from the use of this software.
     8     version 2.1 of the License, or (at your option) any later version.
     8 
     9 
     9   Permission is granted to anyone to use this software for any purpose,
    10     This library is distributed in the hope that it will be useful,
    10   including commercial applications, and to alter it and redistribute it
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   freely, subject to the following restrictions:
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    12 
    13     Lesser General Public License for more details.
    13   1. The origin of this software must not be misrepresented; you must not
    14 
    14      claim that you wrote the original software. If you use this software
    15     You should have received a copy of the GNU Lesser General Public
    15      in a product, an acknowledgment in the product documentation would be
    16     License along with this library; if not, write to the Free Software
    16      appreciated but is not required.
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    17   2. Altered source versions must be plainly marked as such, and must not be
    18 
    18      misrepresented as being the original software.
    19     Sam Lantinga
    19   3. This notice may not be removed or altered from any source distribution.
    20     slouken@libsdl.org
       
    21 */
    20 */
    22 
       
    23 #define org_hedgewars_hedgeroid org_hedgewars_hedgeroid
       
    24 
       
    25 #include "SDL_config.h"
    21 #include "SDL_config.h"
    26 #include "SDL_stdinc.h"
    22 #include "SDL_stdinc.h"
       
    23 
       
    24 #ifdef __ANDROID__
    27 
    25 
    28 #include "SDL_android.h"
    26 #include "SDL_android.h"
    29 
    27 
    30 extern "C" {
    28 extern "C" {
    31 #include "../../events/SDL_events_c.h"
    29 #include "../../events/SDL_events_c.h"
    32 #include "../../video/android/SDL_androidkeyboard.h"
    30 #include "../../video/android/SDL_androidkeyboard.h"
    33 #include "../../video/android/SDL_androidtouch.h"
    31 #include "../../video/android/SDL_androidtouch.h"
    34 #include "../../video/android/SDL_androidvideo.h"
    32 #include "../../video/android/SDL_androidvideo.h"
    35 
    33 
    36 /* Impelemented in audio/android/SDL_androidaudio.c */
    34 #include <android/log.h>
       
    35 #define LOG_TAG "SDL_android"
       
    36 //#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
       
    37 //#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
       
    38 #define LOGI(...) do {} while (false)
       
    39 #define LOGE(...) do {} while (false)
       
    40 
       
    41 
       
    42 /* Implemented in audio/android/SDL_androidaudio.c */
    37 extern void Android_RunAudioThread();
    43 extern void Android_RunAudioThread();
    38 } // C
    44 } // C
    39 
    45 
    40 /*******************************************************************************
    46 /*******************************************************************************
    41  This file links the Java side of Android with libsdl
    47  This file links the Java side of Android with libsdl
    47 /*******************************************************************************
    53 /*******************************************************************************
    48                                Globals
    54                                Globals
    49 *******************************************************************************/
    55 *******************************************************************************/
    50 static JNIEnv* mEnv = NULL;
    56 static JNIEnv* mEnv = NULL;
    51 static JNIEnv* mAudioEnv = NULL;
    57 static JNIEnv* mAudioEnv = NULL;
       
    58 static JavaVM* mJavaVM;
    52 
    59 
    53 // Main activity
    60 // Main activity
    54 static jclass mActivityClass;
    61 static jclass mActivityClass;
    55 
    62 
    56 // method signatures
    63 // method signatures
    61 static jmethodID midAudioWriteByteBuffer;
    68 static jmethodID midAudioWriteByteBuffer;
    62 static jmethodID midAudioQuit;
    69 static jmethodID midAudioQuit;
    63 
    70 
    64 // Accelerometer data storage
    71 // Accelerometer data storage
    65 static float fLastAccelerometer[3];
    72 static float fLastAccelerometer[3];
    66 
    73 static bool bHasNewData;
    67 
    74 
    68 /*******************************************************************************
    75 /*******************************************************************************
    69                  Functions called by JNI
    76                  Functions called by JNI
    70 *******************************************************************************/
    77 *******************************************************************************/
    71 
    78 
    72 // Library init
    79 // Library init
    73 extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
    80 extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
    74 {
    81 {
       
    82     JNIEnv *env;
       
    83     mJavaVM = vm;
       
    84     LOGI("JNI_OnLoad called");
       
    85     if (mJavaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
       
    86         LOGE("Failed to get the environment using GetEnv()");
       
    87         return -1;
       
    88     }
       
    89 
    75     return JNI_VERSION_1_4;
    90     return JNI_VERSION_1_4;
    76 }
    91 }
    77 
    92 
    78 // Called before SDL_main() to initialize JNI bindings
    93 // Called before SDL_main() to initialize JNI bindings
    79 extern "C" void SDL_Android_Init(JNIEnv* env, jclass cls)
    94 extern "C" void SDL_Android_Init(JNIEnv* env, jclass cls)
    80 {
    95 {
    81     __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init()");
    96     __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init()");
    82 
    97 
    83     mEnv = env;
    98     mEnv = env;
    84     mActivityClass = cls;
    99     mActivityClass = (jclass)env->NewGlobalRef(cls);
    85 
   100 
    86     midCreateGLContext = mEnv->GetStaticMethodID(mActivityClass,
   101     midCreateGLContext = mEnv->GetStaticMethodID(mActivityClass,
    87                                 "createGLContext","(II)Z");
   102                                 "createGLContext","(II)Z");
    88     midFlipBuffers = mEnv->GetStaticMethodID(mActivityClass,
   103     midFlipBuffers = mEnv->GetStaticMethodID(mActivityClass,
    89                                 "flipBuffers","()V");
   104                                 "flipBuffers","()V");
    94     midAudioWriteByteBuffer = mEnv->GetStaticMethodID(mActivityClass,
   109     midAudioWriteByteBuffer = mEnv->GetStaticMethodID(mActivityClass,
    95                                 "audioWriteByteBuffer", "([B)V");
   110                                 "audioWriteByteBuffer", "([B)V");
    96     midAudioQuit = mEnv->GetStaticMethodID(mActivityClass,
   111     midAudioQuit = mEnv->GetStaticMethodID(mActivityClass,
    97                                 "audioQuit", "()V");
   112                                 "audioQuit", "()V");
    98 
   113 
       
   114     bHasNewData = false;
       
   115 
    99     if(!midCreateGLContext || !midFlipBuffers || !midAudioInit ||
   116     if(!midCreateGLContext || !midFlipBuffers || !midAudioInit ||
   100        !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit) {
   117        !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit) {
   101         __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
   118         __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
   102     }
   119     }
       
   120     __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init() finished!");
   103 }
   121 }
   104 
   122 
   105 // Resize
   123 // Resize
   106 extern "C" void Java_org_hedgewars_hedgeroid_SDLActivity_onNativeResize(
   124 extern "C" void Java_org_hedgewars_hedgeroid_SDLActivity_onNativeResize(
   107                                     JNIEnv* env, jclass jcls,
   125                                     JNIEnv* env, jclass jcls,
   138                                     JNIEnv* env, jclass jcls,
   156                                     JNIEnv* env, jclass jcls,
   139                                     jfloat x, jfloat y, jfloat z)
   157                                     jfloat x, jfloat y, jfloat z)
   140 {
   158 {
   141     fLastAccelerometer[0] = x;
   159     fLastAccelerometer[0] = x;
   142     fLastAccelerometer[1] = y;
   160     fLastAccelerometer[1] = y;
   143     fLastAccelerometer[2] = z;   
   161     fLastAccelerometer[2] = z;
       
   162     bHasNewData = true;
   144 }
   163 }
   145 
   164 
   146 // Quit
   165 // Quit
   147 extern "C" void Java_org_hedgewars_hedgeroid_SDLActivity_nativeQuit(
   166 extern "C" void Java_org_hedgewars_hedgeroid_SDLActivity_nativeQuit(
   148                                     JNIEnv* env, jclass cls)
   167                                     JNIEnv* env, jclass cls)
   149 {    
   168 {    
   150     // Inject a SDL_QUIT event
   169     // Inject a SDL_QUIT event
   151     SDL_SendQuit();
   170     SDL_SendQuit();
   152 }
   171 }
   153 
   172 
   154 extern "C" void Java_org_hedgewars_hedgeroid_SDLActivity_nativeRunAudioThread(
   173 // Pause
       
   174 extern "C" void Java_org_libsdl_app_SDLActivity_nativePause(
       
   175                                     JNIEnv* env, jclass cls)
       
   176 {
       
   177     if (Android_Window) {
       
   178         SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
       
   179         SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
       
   180     }
       
   181 }
       
   182 
       
   183 // Resume
       
   184 extern "C" void Java_org_libsdl_app_SDLActivity_nativeResume(
       
   185                                     JNIEnv* env, jclass cls)
       
   186 {
       
   187     if (Android_Window) {
       
   188         SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
       
   189         SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0);
       
   190     }
       
   191 }
       
   192 
       
   193 extern "C" void Java_org_libsdl_app_SDLActivity_nativeRunAudioThread(
   155                                     JNIEnv* env, jclass cls)
   194                                     JNIEnv* env, jclass cls)
   156 {
   195 {
   157     /* This is the audio thread, with a different environment */
   196     /* This is the audio thread, with a different environment */
   158     mAudioEnv = env;
   197     mAudioEnv = env;
   159 
   198 
   186     if (mid) {
   225     if (mid) {
   187         mEnv->CallStaticVoidMethod(mActivityClass, mid, mEnv->NewStringUTF(title));
   226         mEnv->CallStaticVoidMethod(mActivityClass, mid, mEnv->NewStringUTF(title));
   188     }
   227     }
   189 }
   228 }
   190 
   229 
   191 extern "C" void Android_JNI_GetAccelerometerValues(float values[3])
   230 extern "C" SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
   192 {
   231 {
   193     int i;
   232     int i;
   194     for (i = 0; i < 3; ++i) {
   233     SDL_bool retval = SDL_FALSE;
   195         values[i] = fLastAccelerometer[i];
   234 
   196     }
   235     if (bHasNewData) {
       
   236         for (i = 0; i < 3; ++i) {
       
   237             values[i] = fLastAccelerometer[i];
       
   238         }
       
   239         bHasNewData = false;
       
   240         retval = SDL_TRUE;
       
   241     }
       
   242 
       
   243     return retval;
   197 }
   244 }
   198 
   245 
   199 //
   246 //
   200 // Audio support
   247 // Audio support
   201 //
   248 //
   206 
   253 
   207 extern "C" int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
   254 extern "C" int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
   208 {
   255 {
   209     int audioBufferFrames;
   256     int audioBufferFrames;
   210 
   257 
       
   258     int status;
       
   259     JNIEnv *env;
       
   260     static bool isAttached = false;    
       
   261     status = mJavaVM->GetEnv((void **) &env, JNI_VERSION_1_4);
       
   262     if(status < 0) {
       
   263         LOGE("callback_handler: failed to get JNI environment, assuming native thread");
       
   264         status = mJavaVM->AttachCurrentThread(&env, NULL);
       
   265         if(status < 0) {
       
   266             LOGE("callback_handler: failed to attach current thread");
       
   267             return 0;
       
   268         }
       
   269         isAttached = true;
       
   270     }
       
   271 
       
   272     
   211     __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
   273     __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
   212     audioBuffer16Bit = is16Bit;
   274     audioBuffer16Bit = is16Bit;
   213     audioBufferStereo = channelCount > 1;
   275     audioBufferStereo = channelCount > 1;
   214 
   276 
   215     audioBuffer = mEnv->CallStaticObjectMethod(mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames);
   277     audioBuffer = env->CallStaticObjectMethod(mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames);
   216 
   278 
   217     if (audioBuffer == NULL) {
   279     if (audioBuffer == NULL) {
   218         __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: didn't get back a good audio buffer!");
   280         __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: didn't get back a good audio buffer!");
   219         return 0;
   281         return 0;
   220     }
   282     }
   221     audioBuffer = mEnv->NewGlobalRef(audioBuffer);
   283     audioBuffer = env->NewGlobalRef(audioBuffer);
   222 
   284 
   223     jboolean isCopy = JNI_FALSE;
   285     jboolean isCopy = JNI_FALSE;
   224     if (audioBuffer16Bit) {
   286     if (audioBuffer16Bit) {
   225         audioBufferPinned = mEnv->GetShortArrayElements((jshortArray)audioBuffer, &isCopy);
   287         audioBufferPinned = env->GetShortArrayElements((jshortArray)audioBuffer, &isCopy);
   226         audioBufferFrames = mEnv->GetArrayLength((jshortArray)audioBuffer);
   288         audioBufferFrames = env->GetArrayLength((jshortArray)audioBuffer);
   227     } else {
   289     } else {
   228         audioBufferPinned = mEnv->GetByteArrayElements((jbyteArray)audioBuffer, &isCopy);
   290         audioBufferPinned = env->GetByteArrayElements((jbyteArray)audioBuffer, &isCopy);
   229         audioBufferFrames = mEnv->GetArrayLength((jbyteArray)audioBuffer);
   291         audioBufferFrames = env->GetArrayLength((jbyteArray)audioBuffer);
   230     }
   292     }
   231     if (audioBufferStereo) {
   293     if (audioBufferStereo) {
   232         audioBufferFrames /= 2;
   294         audioBufferFrames /= 2;
       
   295     }
       
   296  
       
   297     if (isAttached) {
       
   298         mJavaVM->DetachCurrentThread();
   233     }
   299     }
   234 
   300 
   235     return audioBufferFrames;
   301     return audioBufferFrames;
   236 }
   302 }
   237 
   303 
   253     /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
   319     /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
   254 }
   320 }
   255 
   321 
   256 extern "C" void Android_JNI_CloseAudioDevice()
   322 extern "C" void Android_JNI_CloseAudioDevice()
   257 {
   323 {
   258     mEnv->CallStaticVoidMethod(mActivityClass, midAudioQuit); 
   324     int status;
       
   325     JNIEnv *env;
       
   326     static bool isAttached = false;    
       
   327     status = mJavaVM->GetEnv((void **) &env, JNI_VERSION_1_4);
       
   328     if(status < 0) {
       
   329         LOGE("callback_handler: failed to get JNI environment, assuming native thread");
       
   330         status = mJavaVM->AttachCurrentThread(&env, NULL);
       
   331         if(status < 0) {
       
   332             LOGE("callback_handler: failed to attach current thread");
       
   333             return;
       
   334         }
       
   335         isAttached = true;
       
   336     }
       
   337 
       
   338     env->CallStaticVoidMethod(mActivityClass, midAudioQuit); 
   259 
   339 
   260     if (audioBuffer) {
   340     if (audioBuffer) {
   261         mEnv->DeleteGlobalRef(audioBuffer);
   341         env->DeleteGlobalRef(audioBuffer);
   262         audioBuffer = NULL;
   342         audioBuffer = NULL;
   263         audioBufferPinned = NULL;
   343         audioBufferPinned = NULL;
   264     }
   344     }
   265 }
   345 
       
   346     if (isAttached) {
       
   347         mJavaVM->DetachCurrentThread();
       
   348     }
       
   349 }
       
   350 
       
   351 // Test for an exception and call SDL_SetError with its detail if one occurs
       
   352 static bool Android_JNI_ExceptionOccurred()
       
   353 {
       
   354     jthrowable exception = mEnv->ExceptionOccurred();
       
   355     if (exception != NULL) {
       
   356         jmethodID mid;
       
   357 
       
   358         // Until this happens most JNI operations have undefined behaviour
       
   359         mEnv->ExceptionClear();
       
   360 
       
   361         jclass exceptionClass = mEnv->GetObjectClass(exception);
       
   362         jclass classClass = mEnv->FindClass("java/lang/Class");
       
   363 
       
   364         mid = mEnv->GetMethodID(classClass, "getName", "()Ljava/lang/String;");
       
   365         jstring exceptionName = (jstring)mEnv->CallObjectMethod(exceptionClass, mid);
       
   366         const char* exceptionNameUTF8 = mEnv->GetStringUTFChars(exceptionName, 0);
       
   367 
       
   368         mid = mEnv->GetMethodID(exceptionClass, "getMessage", "()Ljava/lang/String;");
       
   369         jstring exceptionMessage = (jstring)mEnv->CallObjectMethod(exception, mid);
       
   370 
       
   371         if (exceptionMessage != NULL) {
       
   372             const char* exceptionMessageUTF8 = mEnv->GetStringUTFChars(
       
   373                     exceptionMessage, 0);
       
   374             SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
       
   375             mEnv->ReleaseStringUTFChars(exceptionMessage, exceptionMessageUTF8);
       
   376             mEnv->DeleteLocalRef(exceptionMessage);
       
   377         } else {
       
   378             SDL_SetError("%s", exceptionNameUTF8);
       
   379         }
       
   380 
       
   381         mEnv->ReleaseStringUTFChars(exceptionName, exceptionNameUTF8);
       
   382         mEnv->DeleteLocalRef(exceptionName);
       
   383         mEnv->DeleteLocalRef(classClass);
       
   384         mEnv->DeleteLocalRef(exceptionClass);
       
   385         mEnv->DeleteLocalRef(exception);
       
   386 
       
   387         return true;
       
   388     }
       
   389 
       
   390     return false;
       
   391 }
       
   392 
       
   393 static int Android_JNI_FileOpen(SDL_RWops* ctx)
       
   394 {
       
   395     int result = 0;
       
   396 
       
   397     jmethodID mid;
       
   398     jobject context;
       
   399     jobject assetManager;
       
   400     jobject inputStream;
       
   401     jclass channels;
       
   402     jobject readableByteChannel;
       
   403     jstring fileNameJString;
       
   404 
       
   405     bool allocatedLocalFrame = false;
       
   406 
       
   407     if (mEnv->PushLocalFrame(16) < 0) {
       
   408         SDL_SetError("Failed to allocate enough JVM local references");
       
   409         goto failure;
       
   410     } else {
       
   411         allocatedLocalFrame = true;
       
   412     }
       
   413 
       
   414     fileNameJString = (jstring)ctx->hidden.androidio.fileName;
       
   415 
       
   416     // context = SDLActivity.getContext();
       
   417     mid = mEnv->GetStaticMethodID(mActivityClass,
       
   418             "getContext","()Landroid/content/Context;");
       
   419     context = mEnv->CallStaticObjectMethod(mActivityClass, mid);
       
   420 
       
   421     // assetManager = context.getAssets();
       
   422     mid = mEnv->GetMethodID(mEnv->GetObjectClass(context),
       
   423             "getAssets", "()Landroid/content/res/AssetManager;");
       
   424     assetManager = mEnv->CallObjectMethod(context, mid);
       
   425 
       
   426     // inputStream = assetManager.open(<filename>);
       
   427     mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager),
       
   428             "open", "(Ljava/lang/String;)Ljava/io/InputStream;");
       
   429     inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString);
       
   430     if (Android_JNI_ExceptionOccurred()) {
       
   431         goto failure;
       
   432     }
       
   433 
       
   434     ctx->hidden.androidio.inputStream = inputStream;
       
   435     ctx->hidden.androidio.inputStreamRef = mEnv->NewGlobalRef(inputStream);
       
   436 
       
   437     // Despite all the visible documentation on [Asset]InputStream claiming
       
   438     // that the .available() method is not guaranteed to return the entire file
       
   439     // size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ...
       
   440     // android/apis/content/ReadAsset.java imply that Android's
       
   441     // AssetInputStream.available() /will/ always return the total file size
       
   442 
       
   443     // size = inputStream.available();
       
   444     mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
       
   445             "available", "()I");
       
   446     ctx->hidden.androidio.size = mEnv->CallIntMethod(inputStream, mid);
       
   447     if (Android_JNI_ExceptionOccurred()) {
       
   448         goto failure;
       
   449     }
       
   450 
       
   451     // readableByteChannel = Channels.newChannel(inputStream);
       
   452     channels = mEnv->FindClass("java/nio/channels/Channels");
       
   453     mid = mEnv->GetStaticMethodID(channels,
       
   454             "newChannel",
       
   455             "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
       
   456     readableByteChannel = mEnv->CallStaticObjectMethod(
       
   457             channels, mid, inputStream);
       
   458     if (Android_JNI_ExceptionOccurred()) {
       
   459         goto failure;
       
   460     }
       
   461 
       
   462     ctx->hidden.androidio.readableByteChannel = readableByteChannel;
       
   463     ctx->hidden.androidio.readableByteChannelRef =
       
   464         mEnv->NewGlobalRef(readableByteChannel);
       
   465 
       
   466     // Store .read id for reading purposes
       
   467     mid = mEnv->GetMethodID(mEnv->GetObjectClass(readableByteChannel),
       
   468             "read", "(Ljava/nio/ByteBuffer;)I");
       
   469     ctx->hidden.androidio.readMethod = mid;
       
   470 
       
   471     ctx->hidden.androidio.position = 0;
       
   472 
       
   473     if (false) {
       
   474 failure:
       
   475         result = -1;
       
   476 
       
   477         mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
       
   478 
       
   479         if(ctx->hidden.androidio.inputStreamRef != NULL) {
       
   480             mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
       
   481         }
       
   482     }
       
   483 
       
   484     if (allocatedLocalFrame) {
       
   485         mEnv->PopLocalFrame(NULL);
       
   486     }
       
   487 
       
   488     return result;
       
   489 }
       
   490 
       
   491 extern "C" int Android_JNI_FileOpen(SDL_RWops* ctx,
       
   492         const char* fileName, const char*)
       
   493 {
       
   494     if (!ctx) {
       
   495         return -1;
       
   496     }
       
   497 
       
   498     jstring fileNameJString = mEnv->NewStringUTF(fileName);
       
   499     ctx->hidden.androidio.fileName = fileNameJString;
       
   500     ctx->hidden.androidio.fileNameRef = mEnv->NewGlobalRef(fileNameJString);
       
   501     ctx->hidden.androidio.inputStreamRef = NULL;
       
   502     mEnv->DeleteLocalRef(fileNameJString);
       
   503 
       
   504     return Android_JNI_FileOpen(ctx);
       
   505 }
       
   506 
       
   507 extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
       
   508         size_t size, size_t maxnum)
       
   509 {
       
   510     int bytesRemaining = size * maxnum;
       
   511     int bytesRead = 0;
       
   512 
       
   513     jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannel;
       
   514     jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
       
   515     jobject byteBuffer = mEnv->NewDirectByteBuffer(buffer, bytesRemaining);
       
   516 
       
   517     while (bytesRemaining > 0) {
       
   518         // result = readableByteChannel.read(...);
       
   519         int result = mEnv->CallIntMethod(readableByteChannel, readMethod, byteBuffer);
       
   520 
       
   521         if (Android_JNI_ExceptionOccurred()) {
       
   522             mEnv->DeleteLocalRef(byteBuffer);
       
   523             return 0;
       
   524         }
       
   525 
       
   526         if (result < 0) {
       
   527             break;
       
   528         }
       
   529 
       
   530         bytesRemaining -= result;
       
   531         bytesRead += result;
       
   532         ctx->hidden.androidio.position += result;
       
   533     }
       
   534 
       
   535     mEnv->DeleteLocalRef(byteBuffer);
       
   536 
       
   537     return bytesRead / size;
       
   538 }
       
   539 
       
   540 extern "C" size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer,
       
   541         size_t size, size_t num)
       
   542 {
       
   543     SDL_SetError("Cannot write to Android package filesystem");
       
   544     return 0;
       
   545 }
       
   546 
       
   547 static int Android_JNI_FileClose(SDL_RWops* ctx, bool release)
       
   548 {
       
   549     int result = 0;
       
   550 
       
   551     if (ctx) {
       
   552         if (release) {
       
   553             mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
       
   554         }
       
   555 
       
   556         jobject inputStream = (jobject)ctx->hidden.androidio.inputStream;
       
   557 
       
   558         // inputStream.close();
       
   559         jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
       
   560                 "close", "()V");
       
   561         mEnv->CallVoidMethod(inputStream, mid);
       
   562         mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
       
   563         mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef);
       
   564         if (Android_JNI_ExceptionOccurred()) {
       
   565             result = -1;
       
   566         }
       
   567 
       
   568         if (release) {
       
   569             SDL_FreeRW(ctx);
       
   570         }
       
   571     }
       
   572 
       
   573     return result;
       
   574 }
       
   575 
       
   576 
       
   577 extern "C" long Android_JNI_FileSeek(SDL_RWops* ctx, long offset, int whence)
       
   578 {
       
   579     long newPosition;
       
   580 
       
   581     switch (whence) {
       
   582         case RW_SEEK_SET:
       
   583             newPosition = offset;
       
   584             break;
       
   585         case RW_SEEK_CUR:
       
   586             newPosition = ctx->hidden.androidio.position + offset;
       
   587             break;
       
   588         case RW_SEEK_END:
       
   589             newPosition = ctx->hidden.androidio.size + offset;
       
   590             break;
       
   591         default:
       
   592             SDL_SetError("Unknown value for 'whence'");
       
   593             return -1;
       
   594     }
       
   595     if (newPosition < 0) {
       
   596         newPosition = 0;
       
   597     }
       
   598     if (newPosition > ctx->hidden.androidio.size) {
       
   599         newPosition = ctx->hidden.androidio.size;
       
   600     }
       
   601 
       
   602     long movement = newPosition - ctx->hidden.androidio.position;
       
   603     jobject inputStream = (jobject)ctx->hidden.androidio.inputStream;
       
   604 
       
   605     if (movement > 0) {
       
   606         unsigned char buffer[1024];
       
   607 
       
   608         // The easy case where we're seeking forwards
       
   609         while (movement > 0) {
       
   610             long amount = (long) sizeof (buffer);
       
   611             if (amount > movement) {
       
   612                 amount = movement;
       
   613             }
       
   614             size_t result = Android_JNI_FileRead(ctx, buffer, 1, amount);
       
   615 
       
   616             if (result <= 0) {
       
   617                 // Failed to read/skip the required amount, so fail
       
   618                 return -1;
       
   619             }
       
   620 
       
   621             movement -= result;
       
   622         }
       
   623     } else if (movement < 0) {
       
   624         // We can't seek backwards so we have to reopen the file and seek
       
   625         // forwards which obviously isn't very efficient
       
   626         Android_JNI_FileClose(ctx, false);
       
   627         Android_JNI_FileOpen(ctx);
       
   628         Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
       
   629     }
       
   630 
       
   631     ctx->hidden.androidio.position = newPosition;
       
   632 
       
   633     return ctx->hidden.androidio.position;
       
   634 }
       
   635 
       
   636 extern "C" int Android_JNI_FileClose(SDL_RWops* ctx)
       
   637 {
       
   638     return Android_JNI_FileClose(ctx, true);
       
   639 }
       
   640 
       
   641 #endif /* __ANDROID__ */
   266 
   642 
   267 /* vi: set ts=4 sw=4 expandtab: */
   643 /* vi: set ts=4 sw=4 expandtab: */