Fixes Bug 1896 - Android app is running while the screen is locked
authorGabriel Jacobo <gabomdq@gmail.com>
Sat, 06 Jul 2013 15:22:49 -0300
changeset 7358 1aadd7170248
parent 7357 9bd0b241c941
child 7359 96ac247b672e
Fixes Bug 1896 - Android app is running while the screen is locked Original patch by ny00@outlook.com
android-project/src/org/libsdl/app/SDLActivity.java
--- a/android-project/src/org/libsdl/app/SDLActivity.java	Sat Jul 06 16:05:09 2013 +0200
+++ b/android-project/src/org/libsdl/app/SDLActivity.java	Sat Jul 06 15:22:49 2013 -0300
@@ -28,7 +28,7 @@
     private static final String TAG = "SDL";
 
     // Keep track of the paused state
-    public static boolean mIsPaused = false;
+    public static boolean mIsPaused = false, mIsSurfaceReady = false;
 
     // Main components
     protected static SDLActivity mSingleton;
@@ -70,6 +70,7 @@
         mSingleton = this;
 
         // Set up the surface
+        mEGLSurface = EGL10.EGL_NO_SURFACE;
         mSurface = new SDLSurface(getApplication());
 
         mLayout = new AbsoluteLayout(this);
@@ -83,14 +84,14 @@
     protected void onPause() {
         Log.v("SDL", "onPause()");
         super.onPause();
-        // Don't call SDLActivity.nativePause(); here, it will be called by SDLSurface::surfaceDestroyed
+        SDLActivity.handlePause();
     }
 
     @Override
     protected void onResume() {
         Log.v("SDL", "onResume()");
         super.onResume();
-        // Don't call SDLActivity.nativeResume(); here, it will be called via SDLSurface::surfaceChanged->SDLActivity::startApp
+        SDLActivity.handleResume();
     }
 
     @Override
@@ -120,6 +121,32 @@
         }
     }
 
+
+    /** Called by onPause or surfaceDestroyed. Even if surfaceDestroyed
+     *  is the first to be called, mIsSurfaceReady should still be set
+     *  to 'true' during the call to onPause (in a usual scenario).
+     */
+    public static void handlePause() {
+        if (!SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady) {
+            SDLActivity.mIsPaused = true;
+            SDLActivity.nativePause();
+            mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, false);
+        }
+    }
+
+    /** Called by onResume or surfaceCreated. An actual resume should be done only when the surface is ready.
+     * Note: Some Android variants may send multiple surfaceChanged events, so we don't need to resume
+     * every time we get one of those events, only if it comes after surfaceDestroyed
+     */
+    public static void handleResume() {
+        if (SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady) {
+            SDLActivity.mIsPaused = false;
+            SDLActivity.nativeResume();
+            mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+        }
+    }
+
+
     // Messages from the SDLMain thread
     static final int COMMAND_CHANGE_TITLE = 1;
     static final int COMMAND_UNUSED = 2;
@@ -227,24 +254,6 @@
         return mSingleton;
     }
 
-    public static void startApp() {
-        // Start up the C app thread
-        if (mSDLThread == null) {
-            mSDLThread = new Thread(new SDLMain(), "SDLThread");
-            mSDLThread.start();
-        }
-        else {
-            /*
-             * Some Android variants may send multiple surfaceChanged events, so we don't need to resume every time
-             * every time we get one of those events, only if it comes after surfaceDestroyed
-             */
-            if (mIsPaused) {
-                SDLActivity.nativeResume();
-                SDLActivity.mIsPaused = false;
-            }
-        }
-    }
-    
     static class ShowTextInputTask implements Runnable {
         /*
          * This is used to regulate the pan&scan method to have some offset from
@@ -343,25 +352,30 @@
             EGL10 egl = (EGL10)EGLContext.getEGL();
             if (SDLActivity.mEGLContext == null) createEGLContext();
 
-            Log.v("SDL", "Creating new EGL Surface");
-            EGLSurface surface = egl.eglCreateWindowSurface(SDLActivity.mEGLDisplay, SDLActivity.mEGLConfig, SDLActivity.mSurface, null);
-            if (surface == EGL10.EGL_NO_SURFACE) {
-                Log.e("SDL", "Couldn't create surface");
-                return false;
+            if (SDLActivity.mEGLSurface == EGL10.EGL_NO_SURFACE) {
+                Log.v("SDL", "Creating new EGL Surface");
+                SDLActivity.mEGLSurface = egl.eglCreateWindowSurface(SDLActivity.mEGLDisplay, SDLActivity.mEGLConfig, SDLActivity.mSurface, null);
+                if (SDLActivity.mEGLSurface == EGL10.EGL_NO_SURFACE) {
+                    Log.e("SDL", "Couldn't create surface");
+                    return false;
+                }
             }
+            else Log.v("SDL", "EGL Surface remains valid");
 
             if (egl.eglGetCurrentContext() != SDLActivity.mEGLContext) {
-                if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, surface, SDLActivity.mEGLContext)) {
+                if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, SDLActivity.mEGLSurface, SDLActivity.mEGLSurface, SDLActivity.mEGLContext)) {
                     Log.e("SDL", "Old EGL Context doesnt work, trying with a new one");
                     // TODO: Notify the user via a message that the old context could not be restored, and that textures need to be manually restored.
                     createEGLContext();
-                    if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, surface, SDLActivity.mEGLContext)) {
+                    if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, SDLActivity.mEGLSurface, SDLActivity.mEGLSurface, SDLActivity.mEGLContext)) {
                         Log.e("SDL", "Failed making EGL Context current");
                         return false;
                     }
                 }
+                else Log.v("SDL", "EGL Context made current");
             }
-            SDLActivity.mEGLSurface = surface;
+            else Log.v("SDL", "EGL Context remains current");
+
             return true;
         } else {
             Log.e("SDL", "Surface creation failed, display = " + SDLActivity.mEGLDisplay + ", config = " + SDLActivity.mEGLConfig);
@@ -533,18 +547,27 @@
     public void surfaceCreated(SurfaceHolder holder) {
         Log.v("SDL", "surfaceCreated()");
         holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
-        enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+        // Set mIsSurfaceReady to 'true' *before* any call to handleResume
+        SDLActivity.mIsSurfaceReady = true;
     }
 
     // Called when we lose the surface
     @Override
     public void surfaceDestroyed(SurfaceHolder holder) {
         Log.v("SDL", "surfaceDestroyed()");
-        if (!SDLActivity.mIsPaused) {
-            SDLActivity.mIsPaused = true;
-            SDLActivity.nativePause();
-        }
-        enableSensor(Sensor.TYPE_ACCELEROMETER, false);
+        // Call this *before* setting mIsSurfaceReady to 'false'
+        SDLActivity.handlePause();
+        SDLActivity.mIsSurfaceReady = false;
+
+        /* We have to clear the current context and destroy the egl surface here
+         * Otherwise there's BAD_NATIVE_WINDOW errors coming from eglCreateWindowSurface on resume
+         * Ref: http://stackoverflow.com/questions/8762589/eglcreatewindowsurface-on-ics-and-switching-from-2d-to-3d
+         */
+        
+        EGL10 egl = (EGL10)EGLContext.getEGL();
+        egl.eglMakeCurrent(SDLActivity.mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+        egl.eglDestroySurface(SDLActivity.mEGLDisplay, SDLActivity.mEGLSurface);
+        SDLActivity.mEGLSurface = EGL10.EGL_NO_SURFACE;
     }
 
     // Called when the surface is resized
@@ -603,7 +626,22 @@
         SDLActivity.onNativeResize(width, height, sdlFormat);
         Log.v("SDL", "Window size:" + width + "x"+height);
 
-        SDLActivity.startApp();
+        // Set mIsSurfaceReady to 'true' *before* making a call to handleResume
+        SDLActivity.mIsSurfaceReady = true;
+
+        if (SDLActivity.mSDLThread == null) {
+            // This is the entry point to the C app.
+            // Start up the C app thread and enable sensor input for the first time
+
+            SDLActivity.mSDLThread = new Thread(new SDLMain(), "SDLThread");
+            enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+            SDLActivity.mSDLThread.start();
+        } else {
+            // The app already exists, we resume via handleResume
+            // Multiple sequential calls to surfaceChanged are handled internally by handleResume
+
+            SDLActivity.handleResume();
+        }
     }
 
     // unused