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