From 16c3b540782a59e084bc5835c4c32be456306011 Mon Sep 17 00:00:00 2001 From: pelya Date: Wed, 29 Sep 2010 18:30:34 +0300 Subject: [PATCH] Background saving/restoring fully works (And pauses audio) --- project/AndroidManifest.xml | 4 +- .../opentyrian/AndroidAppSettings.cfg | 4 +- .../sdl/sdl-1.3/include/SDL_config_android.h | 26 ++++++++ .../sdl/sdl-1.3/include/SDL_screenkeyboard.h | 4 +- .../src/audio/android/SDL_androidaudio.c | 22 +++++-- .../src/video/android/SDL_androidvideo.c | 28 +++++++++ project/src/Audio.java | 20 +++++++ readme.txt | 59 +++++++++++++++++-- 8 files changed, 151 insertions(+), 16 deletions(-) diff --git a/project/AndroidManifest.xml b/project/AndroidManifest.xml index e05fbab3b..725c6a77f 100644 --- a/project/AndroidManifest.xml +++ b/project/AndroidManifest.xml @@ -1,8 +1,8 @@ #include + #define SDL_VIDEO_DRIVER_ANDROID 1 #define SDL_VIDEO_OPENGL_ES 1 #define SDL_VIDEO_RENDER_OGL_ES 1 @@ -159,4 +160,29 @@ #undef SDL_ALTIVEC_BLITTERS #define SDL_ASSEMBLY_ROUTINES 1 // There is no assembly code for Arm CPU yet +/* Prototypes for Android-specific functions */ + +#include "begin_code.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* +Sets callbacks to be called when OS decides to put application to background, and restored to foreground. +*/ +typedef void ( * SDL_ANDROID_ApplicationPutToBackgroundCallback_t ) (void); + +extern DECLSPEC int SDLCALL SDL_ANDROID_SetApplicationPutToBackgroundCallback( + SDL_ANDROID_ApplicationPutToBackgroundCallback_t appPutToBackground, + SDL_ANDROID_ApplicationPutToBackgroundCallback_t appRestored ); + +/* Use these functions instead of setting volume to 0, that will save CPU and battery on device */ +extern DECLSPEC int SDLCALL SDL_ANDROID_PauseAudioPlayback(void); +extern DECLSPEC int SDLCALL SDL_ANDROID_ResumeAudioPlayback(void); + +#ifdef __cplusplus +} +#endif +#include "close_code.h" + #endif /* _SDL_config_minimal_h */ diff --git a/project/sdl/sdl-1.3/include/SDL_screenkeyboard.h b/project/sdl/sdl-1.3/include/SDL_screenkeyboard.h index edc9db0b7..5ce68e369 100644 --- a/project/sdl/sdl-1.3/include/SDL_screenkeyboard.h +++ b/project/sdl/sdl-1.3/include/SDL_screenkeyboard.h @@ -27,7 +27,7 @@ #include "SDL_video.h" #include "SDL_keysym.h" -/* Android-specific functions, mainly on-screen keyboard exposed to the application */ +/* On-screen keyboard exposed to the application, it's yet available on Android platform only */ #include "begin_code.h" /* Set up for C function definitions, even when using C++ */ @@ -74,4 +74,4 @@ extern DECLSPEC int SDLCALL SDL_ANDROID_GetScreenKeyboardSize(); #endif #include "close_code.h" -#endif /* _SDL_config_minimal_h */ +#endif diff --git a/project/sdl/sdl-1.3/src/audio/android/SDL_androidaudio.c b/project/sdl/sdl-1.3/src/audio/android/SDL_androidaudio.c index 1ada96a6a..c0a824f48 100644 --- a/project/sdl/sdl-1.3/src/audio/android/SDL_androidaudio.c +++ b/project/sdl/sdl-1.3/src/audio/android/SDL_androidaudio.c @@ -137,6 +137,8 @@ static JavaVM *jniVM = NULL; static jobject JavaAudioThread = NULL; static jmethodID JavaInitAudio = NULL; static jmethodID JavaDeinitAudio = NULL; +static jmethodID JavaPauseAudioPlayback = NULL; +static jmethodID JavaResumeAudioPlayback = NULL; static Uint8 *ANDROIDAUD_GetAudioBuf(_THIS) @@ -301,6 +303,20 @@ static void ANDROIDAUD_PlayAudio(_THIS) __android_log_print(ANDROID_LOG_INFO, "libSDL", "ANDROIDAUD_PlayAudio() JNI returns a copy of byte array - that's slow"); } +int SDL_ANDROID_PauseAudioPlayback(void) +{ + JNIEnv * jniEnv = NULL; + (*jniVM)->AttachCurrentThread(jniVM, &jniEnv, NULL); + return (*jniEnv)->CallIntMethod( jniEnv, JavaAudioThread, JavaPauseAudioPlayback ); +}; +int SDL_ANDROID_ResumeAudioPlayback(void) +{ + JNIEnv * jniEnv = NULL; + (*jniVM)->AttachCurrentThread(jniVM, &jniEnv, NULL); + return (*jniEnv)->CallIntMethod( jniEnv, JavaAudioThread, JavaResumeAudioPlayback ); +}; + + #ifndef SDL_JAVA_PACKAGE_PATH #error You have to define SDL_JAVA_PACKAGE_PATH to your package path with dots replaced with underscores, for example "com_example_SanAngeles" #endif @@ -315,10 +331,8 @@ JNIEXPORT jint JNICALL JAVA_EXPORT_NAME(AudioThread_nativeAudioInitJavaCallbacks JavaAudioThreadClass = (*jniEnv)->GetObjectClass(jniEnv, JavaAudioThread); JavaInitAudio = (*jniEnv)->GetMethodID(jniEnv, JavaAudioThreadClass, "initAudio", "(IIII)I"); JavaDeinitAudio = (*jniEnv)->GetMethodID(jniEnv, JavaAudioThreadClass, "deinitAudio", "()I"); - /* - __android_log_print(ANDROID_LOG_INFO, "libSDL", "nativeAudioInitJavaCallbacks(): JavaAudioThread %p JavaFillBuffer %p JavaInitAudio %p JavaDeinitAudio %p", - JavaAudioThread, JavaFillBuffer, JavaInitAudio, JavaDeinitAudio); - */ + JavaPauseAudioPlayback = (*jniEnv)->GetMethodID(jniEnv, JavaAudioThreadClass, "pauseAudioPlayback", "()I"); + JavaResumeAudioPlayback = (*jniEnv)->GetMethodID(jniEnv, JavaAudioThreadClass, "resumeAudioPlayback", "()I"); } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) diff --git a/project/sdl/sdl-1.3/src/video/android/SDL_androidvideo.c b/project/sdl/sdl-1.3/src/video/android/SDL_androidvideo.c index 07c4cef4e..730af5f32 100644 --- a/project/sdl/sdl-1.3/src/video/android/SDL_androidvideo.c +++ b/project/sdl/sdl-1.3/src/video/android/SDL_androidvideo.c @@ -57,6 +57,18 @@ static jobject JavaRenderer = NULL; static jmethodID JavaSwapBuffers = NULL; static int glContextLost = 0; +static void appPutToBackgroundCallbackDefault(void) +{ + SDL_ANDROID_PauseAudioPlayback(); +} +static void appRestoredCallbackDefault(void) +{ + SDL_ANDROID_ResumeAudioPlayback(); +} + +static SDL_ANDROID_ApplicationPutToBackgroundCallback_t appPutToBackgroundCallback = appPutToBackgroundCallbackDefault; +static SDL_ANDROID_ApplicationPutToBackgroundCallback_t appRestoredCallback = appRestoredCallbackDefault; + int SDL_ANDROID_CallJavaSwapBuffers() { glContextLost = 0; @@ -68,6 +80,7 @@ int SDL_ANDROID_CallJavaSwapBuffers() glContextLost = 0; __android_log_print(ANDROID_LOG_INFO, "libSDL", "OpenGL context recreated, refreshing textures"); SDL_ANDROID_VideoContextRecreated(); + appRestoredCallbackDefault(); } } @@ -97,6 +110,7 @@ JAVA_EXPORT_NAME(DemoRenderer_nativeGlContextLost) ( JNIEnv* env, jobject thiz { __android_log_print(ANDROID_LOG_INFO, "libSDL", "OpenGL context lost, waiting for new OpenGL context"); glContextLost = 1; + appPutToBackgroundCallbackDefault(); SDL_ANDROID_VideoContextLost(); } @@ -112,3 +126,17 @@ JAVA_EXPORT_NAME(DemoRenderer_nativeInitJavaCallbacks) ( JNIEnv* env, jobject t ANDROID_InitOSKeymap(); } + +int SDL_ANDROID_SetApplicationPutToBackgroundCallback( + SDL_ANDROID_ApplicationPutToBackgroundCallback_t appPutToBackground, + SDL_ANDROID_ApplicationPutToBackgroundCallback_t appRestored ) +{ + appPutToBackgroundCallback = appPutToBackgroundCallbackDefault; + appRestoredCallback = appRestoredCallbackDefault; + + if( appPutToBackground ) + appPutToBackgroundCallback = appPutToBackground; + + if( appRestoredCallback ) + appRestoredCallback = appRestored; +} diff --git a/project/src/Audio.java b/project/src/Audio.java index 8e2da1863..9f29bfcb5 100644 --- a/project/src/Audio.java +++ b/project/src/Audio.java @@ -89,6 +89,26 @@ class AudioThread { return 1; } + public int pauseAudioPlayback() + { + if( mAudio != null ) + { + mAudio.pause(); + return 1; + } + return 0; + } + + public int resumeAudioPlayback() + { + if( mAudio != null ) + { + mAudio.play(); + return 1; + } + return 0; + } + private native int nativeAudioInitJavaCallbacks(); } diff --git a/readme.txt b/readme.txt index 7159a2d06..e2f939ac7 100644 --- a/readme.txt +++ b/readme.txt @@ -156,15 +156,55 @@ gdb libsdl.so -ex "list *0x0002ca00" It will output the exact line in your source where the application crashed. +Android Application lifectcle support +===================================== + +Application may be put to background at any time, for example if user gets phone call onto the device. +The application will lose OpenGL context then, and has to re-create it when put to foreground. + +The SDL provides function +SDL_ANDROID_SetApplicationPutToBackgroundCallback( callback_t appPutToBackground, callback_t appRestored ); +where callback_t is function pointer of type "void (*) void". +The default callbacks will call another Android-specific functions: +SDL_ANDROID_PauseAudioPlayback() and SDL_ANDROID_ResumeAudioPlayback() +which will pause and resume audio from HW layer, so appplication does not need to destroy and re-init audio. + +If you're using pure SDL 1.2 API (with or without HW acceleration) you don't need to worry about anything - +the SDL itself will re-create GL textures and fill them with pixel data from existing SDL HW surfaces, +so you may leave the callbacks to defaults. + +If you're using SDL 1.3 API and using SDL_Texture, then the textures pixeldata is lost - you will need +to call SDL_UpdateTexture() to refill texture pixeldata from appRestored() callback for all your textures. +If you're using compatibility API with SDL_Surfaces you don't have to worry about that. + +If you're using SDL with OpenGL with either SDL 1.2 or SDL 1.3, the situation is even more grim - +not only all your GL textures are lost, but all GL matrices, blend modes, etc. has to be re-created. + +OS may decide there's too little free RAM left on device, and kill background applications +without notice, so it vill be good to create temporary savegame etc. from appPutToBackground() callback. + +Also it's a good practice to pause any application audio, especially if the user gets phone call, +and if you won't set your own callbacks the default callbacks will do exactly that. +There are circumstances when you want to avoid that, for example if the application is audio player, +or if application gets some notification over network (for example you're running a game server, +and want a beep when someone connects to you) - you may unpause audio for some short time then. + +The application is not allowed to do any GFX output without OpenGL context (or it will crash), +that's why SDL_Flip() call will block until we're re-acquired context, and the callbacks will be called +from inside SDL_Flip(). so you won't receive SDL_WINDOWEVENT_HIDDEN / SDL_WINDOWEVENT_SHOWN, +because if SDL sends them the application will get them only after SDL_Flip() successfully +re-acquired GL context, and it's too late to pause audio and save application state, +so please use callbacks instead of SDL window events on Android OS. + +The whole idea behind callbacks is that the existing application should not be modified to +operate correctly - the whole time in background will just look to app as one very long SDL_Flip(), +so it's good idea to implement some maximum time cap on game frame, so it won't process +the game to the end level 'till the app is in background, or calculate the difference in time +between appPutToBackground() and appRestored() and update game time variables. + Known bugs ========== -0. Application will crash when you're pressing "Home" button or open/close keyboard -- the correct behavior for Android apps is to stay in memory and go to foreground -when you're launching app again, that's not working yet because -app will lose OpenGL context (there are rumors that it won't lose GL context in 2.1 SDK). -Anyway, SDL should sleep inside SDL_Flip() and re-create all HW textures when it gains back video. - 1. Merge all config screens into single big config screen, make option to rerun config. 2. Fix on-screen keyboard, add more keys and more options, make possible for application to control it. @@ -192,6 +232,13 @@ so the data on your TV will lag halfsecond behind the data on the device screen. 7. Make app data to come inside .apk file in assets instead of downloading it from net. +8. OpenTyrian: + 1. Navigating game menus downwards with trackball skips events, that does not happen + when navigting upwards. + 2. The detail level can be set to WILD by pressing "w" key in gameplay escape menu, expose that through interface. + +9. Ur-Quan Masters: add Russian, Deutsch and Slovak translations: http://wiki.uqm.stack.nl/Translations + Games to port =============