zenilib  0.5.3.0
 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 {
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 
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 {
155 
157  return;
158  }
159 
160  _this = SDL_GetVideoDevice();
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  }
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  */
185 
187  return;
188  }
189 
190  _this = SDL_GetVideoDevice();
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 {
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 */
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 {
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();
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 */
276  }
277 
278  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
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()");
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  */
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 
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 
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  }
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 
539 {
540  return audioBufferPinned;
541 }
542 
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 
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 
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 
903 {
904  return ctx->hidden.androidio.size;
905 }
906 
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 
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 
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 
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 
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 
return
Definition: pngrutil.c:1266
int Android_JNI_FileClose(SDL_RWops *ctx)
GLenum GLint param
Definition: gl2ext.h:1491
GLdouble s
Definition: glew.h:1376
int Android_OnKeyUp(int keycode)
int Android_JNI_SendMessage(int command, int param)
void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: gl2ext.h:961
#define NULL
Definition: ftobjs.h:61
#define EGL_NO_SURFACE
Definition: egl.h:71
GLenum GLsizei const void * pathString
Definition: glew.h:12419
SDL_bool Android_JNI_HasClipboardText()
SDL_bool
Definition: SDL_stdinc.h:116
SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
EGLSurface EGLint x
Definition: eglext.h:293
void Android_JNI_SetActivityTitle(const char *title)
void size_t size
Definition: SDL_rwops.h:74
char * Android_JNI_GetClipboardText()
int Android_OnKeyDown(int keycode)
EGLSurface EGLint EGLint EGLint EGLint height
Definition: eglext.h:293
void Android_OnTouch(int touch_device_id_in, int pointer_finger_id_in, int action, float x, float y, float p)
EGLImageKHR EGLint * name
Definition: eglext.h:284
#define RW_SEEK_END
Definition: SDL_rwops.h:176
void Android_JNI_HideTextInput()
int SDL_SendWindowEvent(SDL_Window *window, Uint8 windowevent, int data1, int data2)
if(!yyg->yy_init)
GLenum func
Definition: SDL_opengl.h:5654
GLsizei const GLchar *const * path
Definition: glew.h:5828
int Android_JNI_FileOpen(SDL_RWops *ctx, const char *fileName, const char *mode)
ret
Definition: glew_str_glx.c:2
SDL_sem * Android_PauseSem
static SDL_VideoDevice * _this
Definition: SDL_video.c:92
EGLNativeWindowType NativeWindowType
Definition: eglplatform.h:117
int Android_JNI_SetupThread(void)
EGLContext EGLenum EGLClientBuffer buffer
Definition: eglext.h:87
GLuint64EXT * result
Definition: glew.h:12708
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
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl2ext.h:848
EGLSurface EGLint EGLint EGLint width
Definition: eglext.h:293
int SDL_SendKeyboardText(const char *text)
Definition: SDL_keyboard.c:784
DECLSPEC char *SDLCALL SDL_strdup(const char *str)
Definition: SDL_string.c:511
DECLSPEC int SDLCALL SDL_Error(SDL_errorcode code)
Definition: SDL_error.c:222
DECLSPEC Uint32 SDLCALL SDL_SemValue(SDL_sem *sem)
Definition: SDL_syssem.c:186
GLfloat GLfloat p
Definition: glew.h:14938
void Android_JNI_CloseAudioDevice()
DECLSPEC void SDLCALL SDL_FreeRW(SDL_RWops *area)
Definition: SDL_rwops.c:637
int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seconds, int *percent)
size_t Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, size_t size, size_t num)
SDL_Window * Android_Window
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum format
Definition: gl2ext.h:845
DECLSPEC int SDLCALL SDL_SetError(const char *fmt,...)
Definition: SDL_error.c:53
void Android_SetScreenResolution(int width, int height, Uint32 format)
DECLSPEC int SDLCALL SDL_strcmp(const char *str1, const char *str2)
Definition: SDL_string.c:910
int x
Definition: SDL_rect.h:65
int w
Definition: SDL_rect.h:66
jmp_buf env
Definition: jumphack.c:12
GLenum GLenum GLenum GLenum GLenum scale
Definition: glew.h:12632
void * Android_JNI_GetAudioBuffer()
GLuint num
Definition: glew.h:2631
#define SEEK_SET
Definition: zconf.h:249
#define SDL_assert(condition)
Definition: SDL_assert.h:159
EGLSurface EGLint EGLint y
Definition: eglext.h:293
ANativeWindow * Android_JNI_GetNativeWindow(void)
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
EGLSurface EGLint void ** value
Definition: eglext.h:301
#define const
Definition: zconf.h:91
GLintptr offset
Definition: glew.h:1668
SDL_sem * Android_ResumeSem
size_t Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, size_t size, size_t maxnum)
void Android_JNI_WriteAudioBuffer()
int h
Definition: SDL_rect.h:66
union SDL_RWops::@74 hidden
Sint64 offset
Definition: SDL_rwops.h:65
GLint level
Definition: gl2ext.h:845
int Android_JNI_SetClipboardText(const char *text)
#define RW_SEEK_SET
Definition: SDL_rwops.h:174
GLint GLint GLint GLint z
Definition: gl2ext.h:1214
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:548
int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
int SDL_SendAppEvent(SDL_EventType eventType)
Definition: SDL_events.c:610
void * driverdata
Definition: SDL_sysvideo.h:99
#define RW_SEEK_CUR
Definition: SDL_rwops.h:175
TParseContext * context
int i
Definition: pngrutil.c:1377
ANativeWindow * native_window
DECLSPEC int SDLCALL SDL_SemPost(SDL_sem *sem)
Definition: SDL_syssem.c:200
int64_t Sint64
A signed 64-bit integer type.
Definition: SDL_stdinc.h:150
int y
Definition: SDL_rect.h:65
JNIEnv * Android_JNI_GetEnv(void)
GLenum mode
Definition: glew.h:2394
int SDL_SendEditingText(const char *text, int start, int length)
Definition: SDL_keyboard.c:807
Sint64 Android_JNI_FileSeek(SDL_RWops *ctx, Sint64 offset, int whence)
EGLSurface egl_surface
A rectangle, with the origin at the upper left.
Definition: SDL_rect.h:63
GLint GLsizei const GLuint64 * values
Definition: glew.h:3473
void Android_JNI_SwapWindow()
int SDL_SendQuit(void)
Definition: SDL_quit.c:115
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
Sint64 Android_JNI_FileSize(SDL_RWops *ctx)
GLsizei size
Definition: gl2ext.h:1467
EGLContext ctx
Definition: eglext.h:87