zenilib: zenilib/jni/external/sdl/src/core/android/SDL_android.cpp Source File

zenilib 0.5.3.0

- Main Page
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
SDL_android.cpp
Go to the documentation of this file.
1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "SDL_config.h"
22 #include "SDL_stdinc.h"
23 #include "SDL_assert.h"
24 #include "SDL_log.h"
25
26 #ifdef __ANDROID__
27
28 #include "SDL_system.h"
29 #include "SDL_android.h"
30 #include <EGL/egl.h>
31
32 extern "C" {
33 #include "../../events/SDL_events_c.h"
34 #include "../../video/android/SDL_androidkeyboard.h"
35 #include "../../video/android/SDL_androidtouch.h"
36 #include "../../video/android/SDL_androidvideo.h"
37
38 #include <android/log.h>
39 #include <pthread.h>
40 #include <sys/types.h>
41 #include <unistd.h>
42 #define LOG_TAG "SDL_android"
43 //#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
44 //#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
45 #define LOGI(...) do {} while (false)
46 #define LOGE(...) do {} while (false)
47
48 /* Uncomment this to log messages entering and exiting methods in this file */
49 //#define DEBUG_JNI
50
51 /* Implemented in audio/android/SDL_androidaudio.c */
52 extern void Android_RunAudioThread();
53
54 static void Android_JNI_ThreadDestroyed(void*);
55 } // C
56
57 /*******************************************************************************
58 This file links the Java side of Android with libsdl
59 *******************************************************************************/
60 #include <jni.h>
61 #include <android/log.h>
62
63
64 /*******************************************************************************
65 Globals
66 *******************************************************************************/
67 static pthread_key_t mThreadKey;
68 static JavaVM* mJavaVM;
69
70 // Main activity
71 static jclass mActivityClass;
72
73 // method signatures
74 static jmethodID midCreateGLContext;
75 static jmethodID midFlipBuffers;
76 static jmethodID midAudioInit;
77 static jmethodID midAudioWriteShortBuffer;
78 static jmethodID midAudioWriteByteBuffer;
79 static jmethodID midAudioQuit;
80
81 // Accelerometer data storage
82 static float fLastAccelerometer[3];
83 static bool bHasNewData;
84
85 /*******************************************************************************
86 Functions called by JNI
87 *******************************************************************************/
88
89 // Library init
90 extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
91 {
92 JNIEnv *env;
93 mJavaVM = vm;
94 LOGI("JNI_OnLoad called");
95 if (mJavaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
96 LOGE("Failed to get the environment using GetEnv()");
97 return -1;
98 }
99 /*
100 * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
101 * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
102 */
103 if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed)) {
104 __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing pthread key");
105 }
106 else {
107 Android_JNI_SetupThread();
108 }
109
110 return JNI_VERSION_1_4;
111 }
112
113 // Called before SDL_main() to initialize JNI bindings
114 extern "C" void SDL_Android_Init(JNIEnv* mEnv, jclass cls)
115 {
116 __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init()");
117
118 Android_JNI_SetupThread();
119
120 mActivityClass = (jclass)mEnv->NewGlobalRef(cls);
121
122 midCreateGLContext = mEnv->GetStaticMethodID(mActivityClass,
123 "createGLContext","(II[I)Z");
124 midFlipBuffers = mEnv->GetStaticMethodID(mActivityClass,
125 "flipBuffers","()V");
126 midAudioInit = mEnv->GetStaticMethodID(mActivityClass,
127 "audioInit", "(IZZI)V");
128 midAudioWriteShortBuffer = mEnv->GetStaticMethodID(mActivityClass,
129 "audioWriteShortBuffer", "([S)V");
130 midAudioWriteByteBuffer = mEnv->GetStaticMethodID(mActivityClass,
131 "audioWriteByteBuffer", "([B)V");
132 midAudioQuit = mEnv->GetStaticMethodID(mActivityClass,
133 "audioQuit", "()V");
134
135 bHasNewData = false;
136
137 if(!midCreateGLContext || !midFlipBuffers || !midAudioInit ||
138 !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit) {
139 __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
140 }
141 __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init() finished!");
142 }
143
144 // Resize
145 extern "C" void Java_org_libsdl_app_SDLActivity_onNativeResize(
146 JNIEnv* env, jclass jcls,
147 jint width, jint height, jint format)
148 {
149 Android_SetScreenResolution(width, height, format);
150 }
151
152 // Keydown
153 extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyDown(
154 JNIEnv* env, jclass jcls, jint keycode)
155 {
156 Android_OnKeyDown(keycode);
157 }
158
159 // Keyup
160 extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyUp(
161 JNIEnv* env, jclass jcls, jint keycode)
162 {
163 Android_OnKeyUp(keycode);
164 }
165
166 // Touch
167 extern "C" void Java_org_libsdl_app_SDLActivity_onNativeTouch(
168 JNIEnv* env, jclass jcls,
169 jint touch_device_id_in, jint pointer_finger_id_in,
170 jint action, jfloat x, jfloat y, jfloat p)
171 {
172 Android_OnTouch(touch_device_id_in, pointer_finger_id_in, action, x, y, p);
173 }
174
175 // Accelerometer
176 extern "C" void Java_org_libsdl_app_SDLActivity_onNativeAccel(
177 JNIEnv* env, jclass jcls,
178 jfloat x, jfloat y, jfloat z)
179 {
180 fLastAccelerometer[0] = x;
181 fLastAccelerometer[1] = y;
182 fLastAccelerometer[2] = z;
183 bHasNewData = true;
184 }
185
186 // Low memory
187 extern "C" void Java_org_libsdl_app_SDLActivity_nativeLowMemory(
188 JNIEnv* env, jclass cls)
189 {
190 SDL_SendAppEvent(SDL_APP_LOWMEMORY);
191 }
192
193 // Quit
194 extern "C" void Java_org_libsdl_app_SDLActivity_nativeQuit(
195 JNIEnv* env, jclass cls)
196 {
197 // Inject a SDL_QUIT event
198 SDL_SendQuit();
199 SDL_SendAppEvent(SDL_APP_TERMINATING);
200 }
201
202 // Pause
203 extern "C" void Java_org_libsdl_app_SDLActivity_nativePause(
204 JNIEnv* env, jclass cls)
205 {
206 if (Android_Window) {
207 /* Signal the pause semaphore so the event loop knows to pause and (optionally) block itself */
208 if (!SDL_SemValue(Android_PauseSem)) SDL_SemPost(Android_PauseSem);
209 SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
210 SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
211 }
212
213 __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
214 SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
215 SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
216 }
217
218 // Resume
219 extern "C" void Java_org_libsdl_app_SDLActivity_nativeResume(
220 JNIEnv* env, jclass cls)
221 {
222 __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()");
223 SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
224 SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
225
226 if (Android_Window) {
227 /* Signal the resume semaphore so the event loop knows to resume and restore the GL Context
228 * We can't restore the GL Context here because it needs to be done on the SDL main thread
229 * and this function will be called from the Java thread instead.
230 */
231 if (!SDL_SemValue(Android_ResumeSem)) SDL_SemPost(Android_ResumeSem);
232 SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
233 SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0);
234 }
235 }
236
237 extern "C" void Java_org_libsdl_app_SDLActivity_nativeRunAudioThread(
238 JNIEnv* env, jclass cls)
239 {
240 /* This is the audio thread, with a different environment */
241 Android_JNI_SetupThread();
242
243 Android_RunAudioThread();
244 }
245
246 extern "C" void Java_org_libsdl_app_SDLInputConnection_nativeCommitText(
247 JNIEnv* env, jclass cls,
248 jstring text, jint newCursorPosition)
249 {
250 const char *utftext = env->GetStringUTFChars(text, NULL);
251
252 SDL_SendKeyboardText(utftext);
253
254 env->ReleaseStringUTFChars(text, utftext);
255 }
256
257 extern "C" void Java_org_libsdl_app_SDLInputConnection_nativeSetComposingText(
258 JNIEnv* env, jclass cls,
259 jstring text, jint newCursorPosition)
260 {
261 const char *utftext = env->GetStringUTFChars(text, NULL);
262
263 SDL_SendEditingText(utftext, 0, 0);
264
265 env->ReleaseStringUTFChars(text, utftext);
266 }
267
268
269
270 /*******************************************************************************
271 Functions called by SDL into Java
272 *******************************************************************************/
273
274 class LocalReferenceHolder
275 {
276 private:
277 static int s_active;
278
279 public:
280 static bool IsActive() {
281 return s_active > 0;
282 }
283
284 public:
285 LocalReferenceHolder(const char *func) : m_env(NULL), m_func(func) {
286 #ifdef DEBUG_JNI
287 SDL_Log("Entering function %s", m_func);
288 #endif
289 }
290 ~LocalReferenceHolder() {
291 #ifdef DEBUG_JNI
292 SDL_Log("Leaving function %s", m_func);
293 #endif
294 if (m_env) {
295 m_env->PopLocalFrame(NULL);
296 --s_active;
297 }
298 }
299
300 bool init(JNIEnv *env, jint capacity = 16) {
301 if (env->PushLocalFrame(capacity) < 0) {
302 SDL_SetError("Failed to allocate enough JVM local references");
303 return false;
304 }
305 ++s_active;
306 m_env = env;
307 return true;
308 }
309
310 protected:
311 JNIEnv *m_env;
312 const char *m_func;
313 };
314 int LocalReferenceHolder::s_active;
315
316 extern "C" SDL_bool Android_JNI_CreateContext(int majorVersion, int minorVersion,
317 int red, int green, int blue, int alpha,
318 int buffer, int depth, int stencil,
319 int buffers, int samples)
320 {
321 JNIEnv *env = Android_JNI_GetEnv();
322
323 jint attribs[] = {
324 EGL_RED_SIZE, red,
325 EGL_GREEN_SIZE, green,
326 EGL_BLUE_SIZE, blue,
327 EGL_ALPHA_SIZE, alpha,
328 EGL_BUFFER_SIZE, buffer,
329 EGL_DEPTH_SIZE, depth,
330 EGL_STENCIL_SIZE, stencil,
331 EGL_SAMPLE_BUFFERS, buffers,
332 EGL_SAMPLES, samples,
333 EGL_RENDERABLE_TYPE, (majorVersion == 1 ? EGL_OPENGL_ES_BIT : EGL_OPENGL_ES2_BIT),
334 EGL_NONE
335 };
336 int len = SDL_arraysize(attribs);
337
338 jintArray array;
339
340 array = env->NewIntArray(len);
341 env->SetIntArrayRegion(array, 0, len, attribs);
342
343 jboolean success = env->CallStaticBooleanMethod(mActivityClass, midCreateGLContext, majorVersion, minorVersion, array);
344
345 env->DeleteLocalRef(array);
346
347 return success ? SDL_TRUE : SDL_FALSE;
348 }
349
350 extern "C" void Android_JNI_SwapWindow()
351 {
352 JNIEnv *mEnv = Android_JNI_GetEnv();
353 mEnv->CallStaticVoidMethod(mActivityClass, midFlipBuffers);
354 }
355
356 extern "C" void Android_JNI_SetActivityTitle(const char *title)
357 {
358 jmethodID mid;
359 JNIEnv *mEnv = Android_JNI_GetEnv();
360 mid = mEnv->GetStaticMethodID(mActivityClass,"setActivityTitle","(Ljava/lang/String;)Z");
361 if (mid) {
362 jstring jtitle = reinterpret_cast<jstring>(mEnv->NewStringUTF(title));
363 mEnv->CallStaticBooleanMethod(mActivityClass, mid, jtitle);
364 mEnv->DeleteLocalRef(jtitle);
365 }
366 }
367
368 extern "C" SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
369 {
370 int i;
371 SDL_bool retval = SDL_FALSE;
372
373 if (bHasNewData) {
374 for (i = 0; i < 3; ++i) {
375 values[i] = fLastAccelerometer[i];
376 }
377 bHasNewData = false;
378 retval = SDL_TRUE;
379 }
380
381 return retval;
382 }
383
384 static void Android_JNI_ThreadDestroyed(void* value) {
385 /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
386 JNIEnv *env = (JNIEnv*) value;
387 if (env != NULL) {
388 mJavaVM->DetachCurrentThread();
389 pthread_setspecific(mThreadKey, NULL);
390 }
391 }
392
393 JNIEnv* Android_JNI_GetEnv(void) {
394 /* From http://developer.android.com/guide/practices/jni.html
395 * All threads are Linux threads, scheduled by the kernel.
396 * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then
397 * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the
398 * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv,
399 * and cannot make JNI calls.
400 * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main"
401 * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread
402 * is a no-op.
403 * Note: You can call this function any number of times for the same thread, there's no harm in it
404 */
405
406 JNIEnv *env;
407 int status = mJavaVM->AttachCurrentThread(&env, NULL);
408 if(status < 0) {
409 LOGE("failed to attach current thread");
410 return 0;
411 }
412
413 return env;
414 }
415
416 int Android_JNI_SetupThread(void) {
417 /* From http://developer.android.com/guide/practices/jni.html
418 * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward,
419 * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be
420 * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific
421 * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.)
422 * Note: The destructor is not called unless the stored value is != NULL
423 * Note: You can call this function any number of times for the same thread, there's no harm in it
424 * (except for some lost CPU cycles)
425 */
426 JNIEnv *env = Android_JNI_GetEnv();
427 pthread_setspecific(mThreadKey, (void*) env);
428 return 1;
429 }
430
431 //
432 // Audio support
433 //
434 static jboolean audioBuffer16Bit = JNI_FALSE;
435 static jboolean audioBufferStereo = JNI_FALSE;
436 static jobject audioBuffer = NULL;
437 static void* audioBufferPinned = NULL;
438
439 extern "C" int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
440 {
441 int audioBufferFrames;
442
443 JNIEnv *env = Android_JNI_GetEnv();
444
445 if (!env) {
446 LOGE("callback_handler: failed to attach current thread");
447 }
448 Android_JNI_SetupThread();
449
450 __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
451 audioBuffer16Bit = is16Bit;
452 audioBufferStereo = channelCount > 1;
453
454 env->CallStaticVoidMethod(mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames);
455
456 /* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on
457 * Android >= 4.2 due to a "stale global reference" error. So now we allocate this buffer directly from this side. */
458
459 if (is16Bit) {
460 jshortArray audioBufferLocal = env->NewShortArray(desiredBufferFrames * (audioBufferStereo ? 2 : 1));
461 if (audioBufferLocal) {
462 audioBuffer = env->NewGlobalRef(audioBufferLocal);
463 env->DeleteLocalRef(audioBufferLocal);
464 }
465 }
466 else {
467 jbyteArray audioBufferLocal = env->NewByteArray(desiredBufferFrames * (audioBufferStereo ? 2 : 1));
468 if (audioBufferLocal) {
469 audioBuffer = env->NewGlobalRef(audioBufferLocal);
470 env->DeleteLocalRef(audioBufferLocal);
471 }
472 }
473
474 if (audioBuffer == NULL) {
475 __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer!");
476 return 0;
477 }
478
479 jboolean isCopy = JNI_FALSE;
480 if (audioBuffer16Bit) {
481 audioBufferPinned = env->GetShortArrayElements((jshortArray)audioBuffer, &isCopy);
482 audioBufferFrames = env->GetArrayLength((jshortArray)audioBuffer);
483 } else {
484 audioBufferPinned = env->GetByteArrayElements((jbyteArray)audioBuffer, &isCopy);
485 audioBufferFrames = env->GetArrayLength((jbyteArray)audioBuffer);
486 }
487 if (audioBufferStereo) {
488 audioBufferFrames /= 2;
489 }
490
491 return audioBufferFrames;
492 }
493
494 extern "C" void * Android_JNI_GetAudioBuffer()
495 {
496 return audioBufferPinned;
497 }
498
499 extern "C" void Android_JNI_WriteAudioBuffer()
500 {
501 JNIEnv *mAudioEnv = Android_JNI_GetEnv();
502
503 if (audioBuffer16Bit) {
504 mAudioEnv->ReleaseShortArrayElements((jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
505 mAudioEnv->CallStaticVoidMethod(mActivityClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
506 } else {
507 mAudioEnv->ReleaseByteArrayElements((jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
508 mAudioEnv->CallStaticVoidMethod(mActivityClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
509 }
510
511 /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
512 }
513
514 extern "C" void Android_JNI_CloseAudioDevice()
515 {
516 JNIEnv *env = Android_JNI_GetEnv();
517
518 env->CallStaticVoidMethod(mActivityClass, midAudioQuit);
519
520 if (audioBuffer) {
521 env->DeleteGlobalRef(audioBuffer);
522 audioBuffer = NULL;
523 audioBufferPinned = NULL;
524 }
525 }
526
527 // Test for an exception and call SDL_SetError with its detail if one occurs
528 // If optional parameter silent is truthy then SDL_SetError() is not called.
529 static bool Android_JNI_ExceptionOccurred(bool silent = false)
530 {
531 SDL_assert(LocalReferenceHolder::IsActive());
532 JNIEnv *mEnv = Android_JNI_GetEnv();
533
534 jthrowable exception = mEnv->ExceptionOccurred();
535 if (exception != NULL) {
536 jmethodID mid;
537
538 // Until this happens most JNI operations have undefined behaviour
539 mEnv->ExceptionClear();
540
541 if (!silent) {
542 jclass exceptionClass = mEnv->GetObjectClass(exception);
543 jclass classClass = mEnv->FindClass("java/lang/Class");
544
545 mid = mEnv->GetMethodID(classClass, "getName", "()Ljava/lang/String;");
546 jstring exceptionName = (jstring)mEnv->CallObjectMethod(exceptionClass, mid);
547 const char* exceptionNameUTF8 = mEnv->GetStringUTFChars(exceptionName, 0);
548
549 mid = mEnv->GetMethodID(exceptionClass, "getMessage", "()Ljava/lang/String;");
550 jstring exceptionMessage = (jstring)mEnv->CallObjectMethod(exception, mid);
551
552 if (exceptionMessage != NULL) {
553 const char* exceptionMessageUTF8 = mEnv->GetStringUTFChars(exceptionMessage, 0);
554 SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
555 mEnv->ReleaseStringUTFChars(exceptionMessage, exceptionMessageUTF8);
556 } else {
557 SDL_SetError("%s", exceptionNameUTF8);
558 }
559
560 mEnv->ReleaseStringUTFChars(exceptionName, exceptionNameUTF8);
561 }
562
563 return true;
564 }
565
566 return false;
567 }
568
569 static int Android_JNI_FileOpen(SDL_RWops* ctx)
570 {
571 LocalReferenceHolder refs(__FUNCTION__);
572 int result = 0;
573
574 jmethodID mid;
575 jobject context;
576 jobject assetManager;
577 jobject inputStream;
578 jclass channels;
579 jobject readableByteChannel;
580 jstring fileNameJString;
581 jobject fd;
582 jclass fdCls;
583 jfieldID descriptor;
584
585 JNIEnv *mEnv = Android_JNI_GetEnv();
586 if (!refs.init(mEnv)) {
587 goto failure;
588 }
589
590 fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef;
591 ctx->hidden.androidio.position = 0;
592
593 // context = SDLActivity.getContext();
594 mid = mEnv->GetStaticMethodID(mActivityClass,
595 "getContext","()Landroid/content/Context;");
596 context = mEnv->CallStaticObjectMethod(mActivityClass, mid);
597
598
599 // assetManager = context.getAssets();
600 mid = mEnv->GetMethodID(mEnv->GetObjectClass(context),
601 "getAssets", "()Landroid/content/res/AssetManager;");
602 assetManager = mEnv->CallObjectMethod(context, mid);
603
604 /* First let's try opening the file to obtain an AssetFileDescriptor.
605 * This method reads the files directly from the APKs using standard *nix calls
606 */
607 mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
608 inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString);
609 if (Android_JNI_ExceptionOccurred(true)) {
610 goto fallback;
611 }
612
613 mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getStartOffset", "()J");
614 ctx->hidden.androidio.offset = mEnv->CallLongMethod(inputStream, mid);
615 if (Android_JNI_ExceptionOccurred(true)) {
616 goto fallback;
617 }
618
619 mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getDeclaredLength", "()J");
620 ctx->hidden.androidio.size = mEnv->CallLongMethod(inputStream, mid);
621 if (Android_JNI_ExceptionOccurred(true)) {
622 goto fallback;
623 }
624
625 mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;");
626 fd = mEnv->CallObjectMethod(inputStream, mid);
627 fdCls = mEnv->GetObjectClass(fd);
628 descriptor = mEnv->GetFieldID(fdCls, "descriptor", "I");
629 ctx->hidden.androidio.fd = mEnv->GetIntField(fd, descriptor);
630 ctx->hidden.androidio.assetFileDescriptorRef = mEnv->NewGlobalRef(inputStream);
631
632 // Seek to the correct offset in the file.
633 lseek(ctx->hidden.androidio.fd, (off_t)ctx->hidden.androidio.offset, SEEK_SET);
634
635 if (false) {
636 fallback:
637 // Disabled log message because of spam on the Nexus 7
638 //__android_log_print(ANDROID_LOG_DEBUG, "SDL", "Falling back to legacy InputStream method for opening file");
639
640 /* Try the old method using InputStream */
641 ctx->hidden.androidio.assetFileDescriptorRef = NULL;
642
643 // inputStream = assetManager.open(<filename>);
644 mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager),
645 "open", "(Ljava/lang/String;I)Ljava/io/InputStream;");
646 inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString, 1 /*ACCESS_RANDOM*/);
647 if (Android_JNI_ExceptionOccurred()) {
648 goto failure;
649 }
650
651 ctx->hidden.androidio.inputStreamRef = mEnv->NewGlobalRef(inputStream);
652
653 // Despite all the visible documentation on [Asset]InputStream claiming
654 // that the .available() method is not guaranteed to return the entire file
655 // size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ...
656 // android/apis/content/ReadAsset.java imply that Android's
657 // AssetInputStream.available() /will/ always return the total file size
658
659 // size = inputStream.available();
660 mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
661 "available", "()I");
662 ctx->hidden.androidio.size = (long)mEnv->CallIntMethod(inputStream, mid);
663 if (Android_JNI_ExceptionOccurred()) {
664 goto failure;
665 }
666
667 // readableByteChannel = Channels.newChannel(inputStream);
668 channels = mEnv->FindClass("java/nio/channels/Channels");
669 mid = mEnv->GetStaticMethodID(channels,
670 "newChannel",
671 "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
672 readableByteChannel = mEnv->CallStaticObjectMethod(
673 channels, mid, inputStream);
674 if (Android_JNI_ExceptionOccurred()) {
675 goto failure;
676 }
677
678 ctx->hidden.androidio.readableByteChannelRef =
679 mEnv->NewGlobalRef(readableByteChannel);
680
681 // Store .read id for reading purposes
682 mid = mEnv->GetMethodID(mEnv->GetObjectClass(readableByteChannel),
683 "read", "(Ljava/nio/ByteBuffer;)I");
684 ctx->hidden.androidio.readMethod = mid;
685 }
686
687 if (false) {
688 failure:
689 result = -1;
690
691 mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
692
693 if(ctx->hidden.androidio.inputStreamRef != NULL) {
694 mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
695 }
696
697 if(ctx->hidden.androidio.readableByteChannelRef != NULL) {
698 mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef);
699 }
700
701 if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) {
702 mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.assetFileDescriptorRef);
703 }
704
705 }
706
707 return result;
708 }
709
710 extern "C" int Android_JNI_FileOpen(SDL_RWops* ctx,
711 const char* fileName, const char*)
712 {
713 LocalReferenceHolder refs(__FUNCTION__);
714 JNIEnv *mEnv = Android_JNI_GetEnv();
715
716 if (!refs.init(mEnv)) {
717 return -1;
718 }
719
720 if (!ctx) {
721 return -1;
722 }
723
724 jstring fileNameJString = mEnv->NewStringUTF(fileName);
725 ctx->hidden.androidio.fileNameRef = mEnv->NewGlobalRef(fileNameJString);
726 ctx->hidden.androidio.inputStreamRef = NULL;
727 ctx->hidden.androidio.readableByteChannelRef = NULL;
728 ctx->hidden.androidio.readMethod = NULL;
729 ctx->hidden.androidio.assetFileDescriptorRef = NULL;
730
731 return Android_JNI_FileOpen(ctx);
732 }
733
734 extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
735 size_t size, size_t maxnum)
736 {
737 LocalReferenceHolder refs(__FUNCTION__);
738
739 if (ctx->hidden.androidio.assetFileDescriptorRef) {
740 size_t bytesMax = size * maxnum;
741 if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) {
742 bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position;
743 }
744 size_t result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
745 if (result > 0) {
746 ctx->hidden.androidio.position += result;
747 return result / size;
748 }
749 return 0;
750 } else {
751 jlong bytesRemaining = (jlong) (size * maxnum);
752 jlong bytesMax = (jlong) (ctx->hidden.androidio.size - ctx->hidden.androidio.position);
753 int bytesRead = 0;
754
755 /* Don't read more bytes than those that remain in the file, otherwise we get an exception */
756 if (bytesRemaining > bytesMax) bytesRemaining = bytesMax;
757
758 JNIEnv *mEnv = Android_JNI_GetEnv();
759 if (!refs.init(mEnv)) {
760 return -1;
761 }
762
763 jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
764 jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
765 jobject byteBuffer = mEnv->NewDirectByteBuffer(buffer, bytesRemaining);
766
767 while (bytesRemaining > 0) {
768 // result = readableByteChannel.read(...);
769 int result = mEnv->CallIntMethod(readableByteChannel, readMethod, byteBuffer);
770
771 if (Android_JNI_ExceptionOccurred()) {
772 return 0;
773 }
774
775 if (result < 0) {
776 break;
777 }
778
779 bytesRemaining -= result;
780 bytesRead += result;
781 ctx->hidden.androidio.position += result;
782 }
783 return bytesRead / size;
784 }
785 }
786
787 extern "C" size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer,
788 size_t size, size_t num)
789 {
790 SDL_SetError("Cannot write to Android package filesystem");
791 return 0;
792 }
793
794 static int Android_JNI_FileClose(SDL_RWops* ctx, bool release)
795 {
796 LocalReferenceHolder refs(__FUNCTION__);
797 int result = 0;
798 JNIEnv *mEnv = Android_JNI_GetEnv();
799
800 if (!refs.init(mEnv)) {
801 return SDL_SetError("Failed to allocate enough JVM local references");
802 }
803
804 if (ctx) {
805 if (release) {
806 mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
807 }
808
809 if (ctx->hidden.androidio.assetFileDescriptorRef) {
810 jobject inputStream = (jobject)ctx->hidden.androidio.assetFileDescriptorRef;
811 jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
812 "close", "()V");
813 mEnv->CallVoidMethod(inputStream, mid);
814 mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.assetFileDescriptorRef);
815 if (Android_JNI_ExceptionOccurred()) {
816 result = -1;
817 }
818 }
819 else {
820 jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef;
821
822 // inputStream.close();
823 jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
824 "close", "()V");
825 mEnv->CallVoidMethod(inputStream, mid);
826 mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
827 mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef);
828 if (Android_JNI_ExceptionOccurred()) {
829 result = -1;
830 }
831 }
832
833 if (release) {
834 SDL_FreeRW(ctx);
835 }
836 }
837
838 return result;
839 }
840
841
842 extern "C" Sint64 Android_JNI_FileSize(SDL_RWops* ctx)
843 {
844 return ctx->hidden.androidio.size;
845 }
846
847 extern "C" Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
848 {
849 if (ctx->hidden.androidio.assetFileDescriptorRef) {
850 switch (whence) {
851 case RW_SEEK_SET:
852 if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
853 offset += ctx->hidden.androidio.offset;
854 break;
855 case RW_SEEK_CUR:
856 offset += ctx->hidden.androidio.position;
857 if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
858 offset += ctx->hidden.androidio.offset;
859 break;
860 case RW_SEEK_END:
861 offset = ctx->hidden.androidio.offset + ctx->hidden.androidio.size + offset;
862 break;
863 default:
864 return SDL_SetError("Unknown value for 'whence'");
865 }
866 whence = SEEK_SET;
867
868 off_t ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
869 if (ret == -1) return -1;
870 ctx->hidden.androidio.position = ret - ctx->hidden.androidio.offset;
871 } else {
872 Sint64 newPosition;
873
874 switch (whence) {
875 case RW_SEEK_SET:
876 newPosition = offset;
877 break;
878 case RW_SEEK_CUR:
879 newPosition = ctx->hidden.androidio.position + offset;
880 break;
881 case RW_SEEK_END:
882 newPosition = ctx->hidden.androidio.size + offset;
883 break;
884 default:
885 return SDL_SetError("Unknown value for 'whence'");
886 }
887
888 /* Validate the new position */
889 if (newPosition < 0) {
890 return SDL_Error(SDL_EFSEEK);
891 }
892 if (newPosition > ctx->hidden.androidio.size) {
893 newPosition = ctx->hidden.androidio.size;
894 }
895
896 Sint64 movement = newPosition - ctx->hidden.androidio.position;
897 if (movement > 0) {
898 unsigned char buffer[4096];
899
900 // The easy case where we're seeking forwards
901 while (movement > 0) {
902 Sint64 amount = sizeof (buffer);
903 if (amount > movement) {
904 amount = movement;
905 }
906 size_t result = Android_JNI_FileRead(ctx, buffer, 1, amount);
907 if (result <= 0) {
908 // Failed to read/skip the required amount, so fail
909 return -1;
910 }
911
912 movement -= result;
913 }
914
915 } else if (movement < 0) {
916 // We can't seek backwards so we have to reopen the file and seek
917 // forwards which obviously isn't very efficient
918 Android_JNI_FileClose(ctx, false);
919 Android_JNI_FileOpen(ctx);
920 Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
921 }
922 }
923
924 return ctx->hidden.androidio.position;
925
926 }
927
928 extern "C" int Android_JNI_FileClose(SDL_RWops* ctx)
929 {
930 return Android_JNI_FileClose(ctx, true);
931 }
932
933 // returns a new global reference which needs to be released later
934 static jobject Android_JNI_GetSystemServiceObject(const char* name)
935 {
936 LocalReferenceHolder refs(__FUNCTION__);
937 JNIEnv* env = Android_JNI_GetEnv();
938 if (!refs.init(env)) {
939 return NULL;
940 }
941
942 jstring service = env->NewStringUTF(name);
943
944 jmethodID mid;
945
946 mid = env->GetStaticMethodID(mActivityClass, "getContext", "()Landroid/content/Context;");
947 jobject context = env->CallStaticObjectMethod(mActivityClass, mid);
948
949 mid = env->GetMethodID(mActivityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
950 jobject manager = env->CallObjectMethod(context, mid, service);
951
952 env->DeleteLocalRef(service);
953
954 return manager ? env->NewGlobalRef(manager) : NULL;
955 }
956
957 #define SETUP_CLIPBOARD(error) \
958 LocalReferenceHolder refs(__FUNCTION__); \
959 JNIEnv* env = Android_JNI_GetEnv(); \
960 if (!refs.init(env)) { \
961 return error; \
962 } \
963 jobject clipboard = Android_JNI_GetSystemServiceObject("clipboard"); \
964 if (!clipboard) { \
965 return error; \
966 }
967
968 extern "C" int Android_JNI_SetClipboardText(const char* text)
969 {
970 SETUP_CLIPBOARD(-1)
971
972 jmethodID mid = env->GetMethodID(env->GetObjectClass(clipboard), "setText", "(Ljava/lang/CharSequence;)V");
973 jstring string = env->NewStringUTF(text);
974 env->CallVoidMethod(clipboard, mid, string);
975 env->DeleteGlobalRef(clipboard);
976 env->DeleteLocalRef(string);
977 return 0;
978 }
979
980 extern "C" char* Android_JNI_GetClipboardText()
981 {
982 SETUP_CLIPBOARD(SDL_strdup(""))
983
984 jmethodID mid = env->GetMethodID(env->GetObjectClass(clipboard), "getText", "()Ljava/lang/CharSequence;");
985 jobject sequence = env->CallObjectMethod(clipboard, mid);
986 env->DeleteGlobalRef(clipboard);
987 if (sequence) {
988 mid = env->GetMethodID(env->GetObjectClass(sequence), "toString", "()Ljava/lang/String;");
989 jstring string = reinterpret_cast<jstring>(env->CallObjectMethod(sequence, mid));
990 const char* utf = env->GetStringUTFChars(string, 0);
991 if (utf) {
992 char* text = SDL_strdup(utf);
993 env->ReleaseStringUTFChars(string, utf);
994 return text;
995 }
996 }
997 return SDL_strdup("");
998 }
999
1000 extern "C" SDL_bool Android_JNI_HasClipboardText()
1001 {
1002 SETUP_CLIPBOARD(SDL_FALSE)
1003
1004 jmethodID mid = env->GetMethodID(env->GetObjectClass(clipboard), "hasText", "()Z");
1005 jboolean has = env->CallBooleanMethod(clipboard, mid);
1006 env->DeleteGlobalRef(clipboard);
1007 return has ? SDL_TRUE : SDL_FALSE;
1008 }
1009
1010
1011 // returns 0 on success or -1 on error (others undefined then)
1012 // returns truthy or falsy value in plugged, charged and battery
1013 // returns the value in seconds and percent or -1 if not available
1014 extern "C" int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent)
1015 {
1016 LocalReferenceHolder refs(__FUNCTION__);
1017 JNIEnv* env = Android_JNI_GetEnv();
1018 if (!refs.init(env)) {
1019 return -1;
1020 }
1021
1022 jmethodID mid;
1023
1024 mid = env->GetStaticMethodID(mActivityClass, "getContext", "()Landroid/content/Context;");
1025 jobject context = env->CallStaticObjectMethod(mActivityClass, mid);
1026
1027 jstring action = env->NewStringUTF("android.intent.action.BATTERY_CHANGED");
1028
1029 jclass cls = env->FindClass("android/content/IntentFilter");
1030
1031 mid = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V");
1032 jobject filter = env->NewObject(cls, mid, action);
1033
1034 env->DeleteLocalRef(action);
1035
1036 mid = env->GetMethodID(mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
1037 jobject intent = env->CallObjectMethod(context, mid, NULL, filter);
1038
1039 env->DeleteLocalRef(filter);
1040
1041 cls = env->GetObjectClass(intent);
1042
1043 jstring iname;
1044 jmethodID imid = env->GetMethodID(cls, "getIntExtra", "(Ljava/lang/String;I)I");
1045
1046 #define GET_INT_EXTRA(var, key) \
1047 iname = env->NewStringUTF(key); \
1048 int var = env->CallIntMethod(intent, imid, iname, -1); \
1049 env->DeleteLocalRef(iname);
1050
1051 jstring bname;
1052 jmethodID bmid = env->GetMethodID(cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
1053
1054 #define GET_BOOL_EXTRA(var, key) \
1055 bname = env->NewStringUTF(key); \
1056 int var = env->CallBooleanMethod(intent, bmid, bname, JNI_FALSE); \
1057 env->DeleteLocalRef(bname);
1058
1059 if (plugged) {
1060 GET_INT_EXTRA(plug, "plugged") // == BatteryManager.EXTRA_PLUGGED (API 5)
1061 if (plug == -1) {
1062 return -1;
1063 }
1064 // 1 == BatteryManager.BATTERY_PLUGGED_AC
1065 // 2 == BatteryManager.BATTERY_PLUGGED_USB
1066 *plugged = (0 < plug) ? 1 : 0;
1067 }
1068
1069 if (charged) {
1070 GET_INT_EXTRA(status, "status") // == BatteryManager.EXTRA_STATUS (API 5)
1071 if (status == -1) {
1072 return -1;
1073 }
1074 // 5 == BatteryManager.BATTERY_STATUS_FULL
1075 *charged = (status == 5) ? 1 : 0;
1076 }
1077
1078 if (battery) {
1079 GET_BOOL_EXTRA(present, "present") // == BatteryManager.EXTRA_PRESENT (API 5)
1080 *battery = present ? 1 : 0;
1081 }
1082
1083 if (seconds) {
1084 *seconds = -1; // not possible
1085 }
1086
1087 if (percent) {
1088 GET_INT_EXTRA(level, "level") // == BatteryManager.EXTRA_LEVEL (API 5)
1089 GET_INT_EXTRA(scale, "scale") // == BatteryManager.EXTRA_SCALE (API 5)
1090 if ((level == -1) || (scale == -1)) {
1091 return -1;
1092 }
1093 *percent = level * 100 / scale;
1094 }
1095
1096 env->DeleteLocalRef(intent);
1097
1098 return 0;
1099 }
1100
1101 // sends message to be handled on the UI event dispatch thread
1102 extern "C" int Android_JNI_SendMessage(int command, int param)
1103 {
1104 JNIEnv *env = Android_JNI_GetEnv();
1105 if (!env) {
1106 return -1;
1107 }
1108 jmethodID mid = env->GetStaticMethodID(mActivityClass, "sendMessage", "(II)Z");
1109 if (!mid) {
1110 return -1;
1111 }
1112 jboolean success = env->CallStaticBooleanMethod(mActivityClass, mid, command, param);
1113 return success ? 0 : -1;
1114 }
1115
1116 extern "C" void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
1117 {
1118 JNIEnv *env = Android_JNI_GetEnv();
1119 if (!env) {
1120 return;
1121 }
1122
1123 jmethodID mid = env->GetStaticMethodID(mActivityClass, "showTextInput", "(IIII)Z");
1124 if (!mid) {
1125 return;
1126 }
1127 env->CallStaticBooleanMethod( mActivityClass, mid,
1128 inputRect->x,
1129 inputRect->y,
1130 inputRect->w,
1131 inputRect->h );
1132 }
1133
1134 extern "C" void Android_JNI_HideTextInput()
1135 {
1136 // has to match Activity constant
1137 const int COMMAND_TEXTEDIT_HIDE = 3;
1138 Android_JNI_SendMessage(COMMAND_TEXTEDIT_HIDE, 0);
1139 }
1140
1142 //
1143 // Functions exposed to SDL applications in SDL_system.h
1144 //
1145
1146 extern "C" void *SDL_AndroidGetJNIEnv()
1147 {
1148 return Android_JNI_GetEnv();
1149 }
1150
1151
1152
1153 extern "C" void *SDL_AndroidGetActivity()
1154 {
1155 /* See SDL_system.h for caveats on using this function. */
1156
1157 jmethodID mid;
1158
1159 JNIEnv *env = Android_JNI_GetEnv();
1160 if (!env) {
1161 return NULL;
1162 }
1163
1164 // return SDLActivity.getContext();
1165 mid = env->GetStaticMethodID(mActivityClass,
1166 "getContext","()Landroid/content/Context;");
1167 return env->CallStaticObjectMethod(mActivityClass, mid);
1168 }
1169
1170 extern "C" const char * SDL_AndroidGetInternalStoragePath()
1171 {
1172 static char *s_AndroidInternalFilesPath = NULL;
1173
1174 if (!s_AndroidInternalFilesPath) {
1175 LocalReferenceHolder refs(__FUNCTION__);
1176 jmethodID mid;
1177 jobject context;
1178 jobject fileObject;
1179 jstring pathString;
1180 const char *path;
1181
1182 JNIEnv *env = Android_JNI_GetEnv();
1183 if (!refs.init(env)) {
1184 return NULL;
1185 }
1186
1187 // context = SDLActivity.getContext();
1188 mid = env->GetStaticMethodID(mActivityClass,
1189 "getContext","()Landroid/content/Context;");
1190 context = env->CallStaticObjectMethod(mActivityClass, mid);
1191
1192 // fileObj = context.getFilesDir();
1193 mid = env->GetMethodID(env->GetObjectClass(context),
1194 "getFilesDir", "()Ljava/io/File;");
1195 fileObject = env->CallObjectMethod(context, mid);
1196 if (!fileObject) {
1197 SDL_SetError("Couldn't get internal directory");
1198 return NULL;
1199 }
1200
1201 // path = fileObject.getAbsolutePath();
1202 mid = env->GetMethodID(env->GetObjectClass(fileObject),
1203 "getAbsolutePath", "()Ljava/lang/String;");
1204 pathString = (jstring)env->CallObjectMethod(fileObject, mid);
1205
1206 path = env->GetStringUTFChars(pathString, NULL);
1207 s_AndroidInternalFilesPath = SDL_strdup(path);
1208 env->ReleaseStringUTFChars(pathString, path);
1209 }
1210 return s_AndroidInternalFilesPath;
1211 }
1212
1213 extern "C" int SDL_AndroidGetExternalStorageState()
1214 {
1215 LocalReferenceHolder refs(__FUNCTION__);
1216 jmethodID mid;
1217 jclass cls;
1218 jstring stateString;
1219 const char *state;
1220 int stateFlags;
1221
1222 JNIEnv *env = Android_JNI_GetEnv();
1223 if (!refs.init(env)) {
1224 return 0;
1225 }
1226
1227 cls = env->FindClass("android/os/Environment");
1228 mid = env->GetStaticMethodID(cls,
1229 "getExternalStorageState", "()Ljava/lang/String;");
1230 stateString = (jstring)env->CallStaticObjectMethod(cls, mid);
1231
1232 state = env->GetStringUTFChars(stateString, NULL);
1233
1234 // Print an info message so people debugging know the storage state
1235 __android_log_print(ANDROID_LOG_INFO, "SDL", "external storage state: %s", state);
1236
1237 if (SDL_strcmp(state, "mounted") == 0) {
1238 stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ |
1239 SDL_ANDROID_EXTERNAL_STORAGE_WRITE;
1240 } else if (SDL_strcmp(state, "mounted_ro") == 0) {
1241 stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ;
1242 } else {
1243 stateFlags = 0;
1244 }
1245 env->ReleaseStringUTFChars(stateString, state);
1246
1247 return stateFlags;
1248 }
1249
1250 extern "C" const char * SDL_AndroidGetExternalStoragePath()
1251 {
1252 static char *s_AndroidExternalFilesPath = NULL;
1253
1254 if (!s_AndroidExternalFilesPath) {
1255 LocalReferenceHolder refs(__FUNCTION__);
1256 jmethodID mid;
1257 jobject context;
1258 jobject fileObject;
1259 jstring pathString;
1260 const char *path;
1261
1262 JNIEnv *env = Android_JNI_GetEnv();
1263 if (!refs.init(env)) {
1264 return NULL;
1265 }
1266
1267 // context = SDLActivity.getContext();
1268 mid = env->GetStaticMethodID(mActivityClass,
1269 "getContext","()Landroid/content/Context;");
1270 context = env->CallStaticObjectMethod(mActivityClass, mid);
1271
1272 // fileObj = context.getExternalFilesDir();
1273 mid = env->GetMethodID(env->GetObjectClass(context),
1274 "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
1275 fileObject = env->CallObjectMethod(context, mid, NULL);
1276 if (!fileObject) {
1277 SDL_SetError("Couldn't get external directory");
1278 return NULL;
1279 }
1280
1281 // path = fileObject.getAbsolutePath();
1282 mid = env->GetMethodID(env->GetObjectClass(fileObject),
1283 "getAbsolutePath", "()Ljava/lang/String;");
1284 pathString = (jstring)env->CallObjectMethod(fileObject, mid);
1285
1286 path = env->GetStringUTFChars(pathString, NULL);
1287 s_AndroidExternalFilesPath = SDL_strdup(path);
1288 env->ReleaseStringUTFChars(pathString, path);
1289 }
1290 return s_AndroidExternalFilesPath;
1291 }
1292
1293 #endif /* __ANDROID__ */
1294
1295 /* vi: set ts=4 sw=4 expandtab: */
SDL_APP_DIDENTERFOREGROUND
Definition: SDL_events.h:83
return
return
Definition: pngrutil.c:1266
Android_JNI_FileClose
int Android_JNI_FileClose(SDL_RWops *ctx)
SDL_APP_WILLENTERFOREGROUND
Definition: SDL_events.h:79
param
GLenum GLint param
Definition: gl2ext.h:1491
Android_OnKeyUp
int Android_OnKeyUp(int keycode)
EGL_RED_SIZE
#define EGL_RED_SIZE
Definition: egl.h:100
Android_JNI_SendMessage
int Android_JNI_SendMessage(int command, int param)
Android_JNI_ShowTextInput
void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
SDL_APP_LOWMEMORY
Definition: SDL_events.h:67
filter
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: gl2ext.h:961
SDL_WINDOWEVENT_MINIMIZED
Definition: SDL_video.h:146
SDL_log.h
NULL
#define NULL
Definition: ftobjs.h:61
pathString
GLenum GLsizei const void * pathString
Definition: glew.h:12419
SDL_RWops
Definition: SDL_rwops.h:52
Android_JNI_HasClipboardText
SDL_bool Android_JNI_HasClipboardText()
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:116
Android_JNI_GetAccelerometerValues
SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
SDL_EFSEEK
Definition: SDL_error.h:60
stencil
GLint GLfloat GLint stencil
Definition: SDL_opengl.h:5886
SDL_TRUE
Definition: SDL_stdinc.h:119
x
EGLSurface EGLint x
Definition: eglext.h:293
EGL_SAMPLE_BUFFERS
#define EGL_SAMPLE_BUFFERS
Definition: egl.h:113
init
static void init(struct bs2b *bs2b)
Definition: bs2b.c:46
Android_JNI_SetActivityTitle
void Android_JNI_SetActivityTitle(const char *title)
SDL_RWops::size
void size_t size
Definition: SDL_rwops.h:74
Android_JNI_GetClipboardText
char * Android_JNI_GetClipboardText()
EGL_ALPHA_SIZE
#define EGL_ALPHA_SIZE
Definition: egl.h:97
EGL_NONE
#define EGL_NONE
Definition: egl.h:119
Android_OnKeyDown
int Android_OnKeyDown(int keycode)
height
EGLSurface EGLint EGLint EGLint EGLint height
Definition: eglext.h:293
Android_OnTouch
void Android_OnTouch(int touch_device_id_in, int pointer_finger_id_in, int action, float x, float y, float p)
name
EGLImageKHR EGLint * name
Definition: eglext.h:284
RW_SEEK_END
#define RW_SEEK_END
Definition: SDL_rwops.h:176
green
GLclampf green
Definition: glew.h:1506
Android_JNI_HideTextInput
void Android_JNI_HideTextInput()
SDL_SendWindowEvent
int SDL_SendWindowEvent(SDL_Window *window, Uint8 windowevent, int data1, int data2)
Definition: SDL_windowevents.c:74
len
GLenum GLsizei len
Definition: glew.h:7035
if
if(!yyg->yy_init)
Definition: glslang_lex.cpp:1037
SDL_FALSE
Definition: SDL_stdinc.h:118
func
GLenum func
Definition: SDL_opengl.h:5654
attribs
const GLint * attribs
Definition: glew.h:13064
path
GLsizei const GLchar *const * path
Definition: glew.h:5828
SDL_config.h
Android_JNI_FileOpen
int Android_JNI_FileOpen(SDL_RWops *ctx, const char *fileName, const char *mode)
ret
ret
Definition: glew_str_glx.c:2
array
GLenum array
Definition: glew.h:8327
EGL_BLUE_SIZE
#define EGL_BLUE_SIZE
Definition: egl.h:98
red
const GLubyte GLuint red
Definition: SDL_glfuncs.h:57
Android_PauseSem
SDL_sem * Android_PauseSem
buffers
const GLuint * buffers
Definition: glew.h:1669
SDL_WINDOWEVENT_FOCUS_GAINED
Definition: SDL_video.h:152
Android_JNI_SetupThread
int Android_JNI_SetupThread(void)
buffer
EGLContext EGLenum EGLClientBuffer buffer
Definition: eglext.h:87
egl.h
EGL_BUFFER_SIZE
#define EGL_BUFFER_SIZE
Definition: egl.h:96
result
GLuint64EXT * result
Definition: glew.h:12708
EGL_OPENGL_ES_BIT
#define EGL_OPENGL_ES_BIT
Definition: egl.h:155
SDL_system.h
SDL_android.h
width
EGLSurface EGLint EGLint EGLint width
Definition: eglext.h:293
SDL_WINDOWEVENT_RESTORED
Definition: SDL_video.h:148
SDL_SendKeyboardText
int SDL_SendKeyboardText(const char *text)
Definition: SDL_keyboard.c:784
SDL_strdup
DECLSPEC char *SDLCALL SDL_strdup(const char *str)
Definition: SDL_string.c:511
SDL_Error
DECLSPEC int SDLCALL SDL_Error(SDL_errorcode code)
Definition: SDL_error.c:222
SDL_SemValue
DECLSPEC Uint32 SDLCALL SDL_SemValue(SDL_sem *sem)
Definition: SDL_syssem.c:186
p
GLfloat GLfloat p
Definition: glew.h:14938
samples
GLsizei samples
Definition: gl2ext.h:970
Android_JNI_CloseAudioDevice
void Android_JNI_CloseAudioDevice()
SDL_FreeRW
DECLSPEC void SDLCALL SDL_FreeRW(SDL_RWops *area)
Definition: SDL_rwops.c:637
Android_JNI_GetPowerInfo
int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seconds, int *percent)
Android_JNI_FileWrite
size_t Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, size_t size, size_t num)
SDL_WINDOWEVENT_FOCUS_LOST
Definition: SDL_video.h:153
Android_Window
SDL_Window * Android_Window
format
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum format
Definition: gl2ext.h:845
SDL_SetError
DECLSPEC int SDLCALL SDL_SetError(const char *fmt,...)
Definition: SDL_error.c:53
Android_SetScreenResolution
void Android_SetScreenResolution(int width, int height, Uint32 format)
depth
GLint GLenum GLsizei GLsizei GLsizei depth
Definition: gl2ext.h:845
SDL_strcmp
DECLSPEC int SDLCALL SDL_strcmp(const char *str1, const char *str2)
Definition: SDL_string.c:910
SDL_Rect::x
int x
Definition: SDL_rect.h:65
alpha
GLclampf GLclampf GLclampf alpha
Definition: glew.h:1506
SDL_Rect::w
int w
Definition: SDL_rect.h:66
EGL_DEPTH_SIZE
#define EGL_DEPTH_SIZE
Definition: egl.h:101
env
jmp_buf env
Definition: jumphack.c:12
scale
GLenum GLenum GLenum GLenum GLenum scale
Definition: glew.h:12632
Android_JNI_GetAudioBuffer
void * Android_JNI_GetAudioBuffer()
num
GLuint num
Definition: glew.h:2631
SEEK_SET
#define SEEK_SET
Definition: zconf.h:249
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:159
EGL_SAMPLES
#define EGL_SAMPLES
Definition: egl.h:112
y
EGLSurface EGLint EGLint y
Definition: eglext.h:293
SDL_arraysize
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:83
SDL_Log
DECLSPEC void SDLCALL SDL_Log(const char *fmt,...)
Log a message with SDL_LOG_CATEGORY_APPLICATION and SDL_LOG_PRIORITY_INFO.
Definition: SDL_log.c:173
value
EGLSurface EGLint void ** value
Definition: eglext.h:301
offset
GLintptr offset
Definition: glew.h:1668
Android_ResumeSem
SDL_sem * Android_ResumeSem
Android_JNI_FileRead
size_t Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, size_t size, size_t maxnum)
Android_JNI_WriteAudioBuffer
void Android_JNI_WriteAudioBuffer()
SDL_Rect::h
int h
Definition: SDL_rect.h:66
SDL_RWops::hidden
union SDL_RWops::@74 hidden
SDL_RWops::offset
Sint64 offset
Definition: SDL_rwops.h:65
level
GLint level
Definition: gl2ext.h:845
EGL_GREEN_SIZE
#define EGL_GREEN_SIZE
Definition: egl.h:99
Android_JNI_SetClipboardText
int Android_JNI_SetClipboardText(const char *text)
RW_SEEK_SET
#define RW_SEEK_SET
Definition: SDL_rwops.h:174
EGL_STENCIL_SIZE
#define EGL_STENCIL_SIZE
Definition: egl.h:102
z
GLint GLint GLint GLint z
Definition: gl2ext.h:1214
Android_JNI_OpenAudioDevice
int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
SDL_APP_WILLENTERBACKGROUND
Definition: SDL_events.h:71
SDL_APP_TERMINATING
Definition: SDL_events.h:63
SDL_SendAppEvent
int SDL_SendAppEvent(SDL_EventType eventType)
Definition: SDL_events.c:610
types.h
RW_SEEK_CUR
#define RW_SEEK_CUR
Definition: SDL_rwops.h:175
context
TParseContext * context
Definition: glslang_tab.cpp:1907
i
int i
Definition: pngrutil.c:1377
SDL_APP_DIDENTERBACKGROUND
Definition: SDL_events.h:75
blue
GLclampf GLclampf blue
Definition: glew.h:1506
SDL_SemPost
DECLSPEC int SDLCALL SDL_SemPost(SDL_sem *sem)
Definition: SDL_syssem.c:200
Sint64
int64_t Sint64
A signed 64-bit integer type.
Definition: SDL_stdinc.h:150
EGL_RENDERABLE_TYPE
#define EGL_RENDERABLE_TYPE
Definition: egl.h:127
SDL_Rect::y
int y
Definition: SDL_rect.h:65
Android_JNI_GetEnv
JNIEnv * Android_JNI_GetEnv(void)
SDL_SendEditingText
int SDL_SendEditingText(const char *text, int start, int length)
Definition: SDL_keyboard.c:807
Android_JNI_FileSeek
Sint64 Android_JNI_FileSeek(SDL_RWops *ctx, Sint64 offset, int whence)
SDL_Rect
A rectangle, with the origin at the upper left.
Definition: SDL_rect.h:63
values
GLint GLsizei const GLuint64 * values
Definition: glew.h:3473
Android_JNI_SwapWindow
void Android_JNI_SwapWindow()
SDL_SendQuit
int SDL_SendQuit(void)
Definition: SDL_quit.c:115
EGL_OPENGL_ES2_BIT
#define EGL_OPENGL_ES2_BIT
Definition: egl.h:157
SDL_assert.h
Android_JNI_FileSize
Sint64 Android_JNI_FileSize(SDL_RWops *ctx)
size
GLsizei size
Definition: gl2ext.h:1467
ctx
EGLContext ctx
Definition: eglext.h:87