Commit 366adae360796a7a8a265a78626e37cbb905c37f

Ryan C. Gordon 2017-04-07T20:17:30

android: More robust fix for screen locking in landscape (thanks, Sylvain!). Fixes Bugzilla #3562. From Sylvain: "With an android landscape application, if you quickly lock, then unlock your device, you can see sometimes a quick glitch: screen badly draws in portrait, then it correctly displays in landscape. Not talking of a smooth rotation, it's a drawing glitch. And you need to have a plain lock screen, with no model nor passphrase. I think it happens because the call to "nativeResume()" occurs sometimes too early. It should be done once you have *all* those three things (in any sequence): - onWindowsFocusChanged() set to true - onResume() called - a valid call to onSurfaceChanged() Currently, this is not the case: you don't need to have onResume() called. Just need to have isPaused, (eg onPaused()). So "isPaused" should be renamed as "isResumedCalled". But you also need some kind of isPaused state to make sure to don't call nativePause() twice (and deadlocks...). There are also redundant checks to "mHasFocus" and some creation of the initialisation thread code from onSurfaceChanged() that could me moved. So here's this patch: - add some states, so we have cleaner transitions. - make sure "onResume()" is called. - some clean up - it also goes faster in pause state (focus changed, onPause, without requiring isSurfaceReady which does seems to be needed). Tested on a few devices and it removes the glitch. But I haven't tested when the activity goes back to "init" state."

diff --git a/android-project/src/org/libsdl/app/SDLActivity.java b/android-project/src/org/libsdl/app/SDLActivity.java
index a49184b..8fbbd78 100644
--- a/android-project/src/org/libsdl/app/SDLActivity.java
+++ b/android-project/src/org/libsdl/app/SDLActivity.java
@@ -36,8 +36,16 @@ import android.content.pm.ActivityInfo;
 public class SDLActivity extends Activity {
     private static final String TAG = "SDL";
 
-    // Keep track of the paused state
-    public static boolean mIsPaused, mIsSurfaceReady, mHasFocus;
+    public static boolean mIsResumedCalled, mIsSurfaceReady, mHasFocus;
+
+    // Handle the state of the native layer
+    public enum NativeState {
+           INIT, RESUMED, PAUSED 
+    }
+
+    public static NativeState mNextNativeState;
+    public static NativeState mCurrentNativeState;
+
     public static boolean mExitCalledFromJava;
 
     /** If shared libraries (e.g. SDL or the native application) could not be loaded. */
@@ -110,9 +118,11 @@ public class SDLActivity extends Activity {
         mAudioRecord = null;
         mExitCalledFromJava = false;
         mBrokenLibraries = false;
-        mIsPaused = false;
+        mIsResumedCalled = false;
         mIsSurfaceReady = false;
         mHasFocus = true;
+        mNextNativeState = NativeState.INIT;
+        mCurrentNativeState = NativeState.INIT;
     }
 
     // Setup
@@ -195,24 +205,28 @@ public class SDLActivity extends Activity {
     protected void onPause() {
         Log.v(TAG, "onPause()");
         super.onPause();
+        mNextNativeState = NativeState.PAUSED;
+        mIsResumedCalled = false;
 
         if (SDLActivity.mBrokenLibraries) {
            return;
         }
 
-        SDLActivity.handlePause();
+        SDLActivity.handleNativeState();
     }
 
     @Override
     protected void onResume() {
         Log.v(TAG, "onResume()");
         super.onResume();
+        mNextNativeState = NativeState.RESUMED;
+        mIsResumedCalled = true;
 
         if (SDLActivity.mBrokenLibraries) {
            return;
         }
 
-        SDLActivity.handleResume();
+        SDLActivity.handleNativeState();
     }
 
 
@@ -227,8 +241,12 @@ public class SDLActivity extends Activity {
 
         SDLActivity.mHasFocus = hasFocus;
         if (hasFocus) {
-            SDLActivity.handleResume();
+           mNextNativeState = NativeState.RESUMED;
+        } else {
+           mNextNativeState = NativeState.PAUSED;
         }
+        
+        SDLActivity.handleNativeState();
     }
 
     @Override
@@ -247,6 +265,9 @@ public class SDLActivity extends Activity {
     protected void onDestroy() {
         Log.v(TAG, "onDestroy()");
 
+        mNextNativeState = NativeState.PAUSED;
+        SDLActivity.handleNativeState();
+
         if (SDLActivity.mBrokenLibraries) {
            super.onDestroy();
            // Reset everything in case the user re opens the app
@@ -295,28 +316,68 @@ public class SDLActivity extends Activity {
         return super.dispatchKeyEvent(event);
     }
 
-    /** 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.handlePause();
+    /* Transition to next state */
+    public static void handleNativeState() {
+
+        if (mNextNativeState == mCurrentNativeState) {
+            // Already in same state, discard.
+            return;
         }
-    }
 
-    /** 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.mHasFocus) {
-            SDLActivity.mIsPaused = false;
-            SDLActivity.nativeResume();
-            mSurface.handleResume();
+        // Try a transition to init state
+        if (mNextNativeState == NativeState.INIT) {
+
+            mCurrentNativeState = mNextNativeState;
+            return;
         }
+
+        // Try a transition to paused state
+        if (mNextNativeState == NativeState.PAUSED) {
+            nativePause();
+            mSurface.handlePause();
+            mCurrentNativeState = mNextNativeState;
+            return;
+        }
+
+        // Try a transition to resumed state
+        if (mNextNativeState == NativeState.RESUMED) {
+
+           if (mIsSurfaceReady && mHasFocus && mIsResumedCalled) {
+
+              if (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
+
+                  final Thread sdlThread = new Thread(new SDLMain(), "SDLThread");
+                  mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+                  sdlThread.start();
+
+                  // Set up a listener thread to catch when the native thread ends
+                  mSDLThread = new Thread(new Runnable(){
+                      @Override
+                      public void run(){
+                          try {
+                              sdlThread.join();
+                          }
+                          catch(Exception e){}
+                          finally{
+                              // Native thread has finished
+                              if (! mExitCalledFromJava) {
+                                  handleNativeExit();
+                              }
+                          }
+                      }
+                  }, "SDLThreadListener");
+                  mSDLThread.start();
+              }
+
+
+              nativeResume();
+              mSurface.handleResume();
+              mCurrentNativeState = mNextNativeState;
+          }
+          return;
+       }
     }
 
     /* The native thread has finished */
@@ -1099,8 +1160,11 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
     @Override
     public void surfaceDestroyed(SurfaceHolder holder) {
         Log.v("SDL", "surfaceDestroyed()");
-        // Call this *before* setting mIsSurfaceReady to 'false'
-        SDLActivity.handlePause();
+
+        // Transition to pause, if needed
+        SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
+        SDLActivity.handleNativeState();
+
         SDLActivity.mIsSurfaceReady = false;
         SDLActivity.onNativeSurfaceDestroyed();
     }
@@ -1193,45 +1257,17 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
 
         if (skip) {
            Log.v("SDL", "Skip .. Surface is not ready.");
+           SDLActivity.mIsSurfaceReady = false;
            return;
         }
-
-
-        // Set mIsSurfaceReady to 'true' *before* making a call to handleResume
+        
+        /* Surface is ready */
         SDLActivity.mIsSurfaceReady = true;
-        SDLActivity.onNativeSurfaceChanged();
-
-
-        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
-
-            final Thread sdlThread = new Thread(new SDLMain(), "SDLThread");
-            enableSensor(Sensor.TYPE_ACCELEROMETER, true);
-            sdlThread.start();
 
-            // Set up a listener thread to catch when the native thread ends
-            SDLActivity.mSDLThread = new Thread(new Runnable(){
-                @Override
-                public void run(){
-                    try {
-                        sdlThread.join();
-                    }
-                    catch(Exception e){}
-                    finally{
-                        // Native thread has finished
-                        if (! SDLActivity.mExitCalledFromJava) {
-                            SDLActivity.handleNativeExit();
-                        }
-                    }
-                }
-            }, "SDLThreadListener");
-            SDLActivity.mSDLThread.start();
-        }
+        /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
+        SDLActivity.onNativeSurfaceChanged();
 
-        if (SDLActivity.mHasFocus && !SDLActivity.mIsPaused) {
-            SDLActivity.handleResume();
-        }
+        SDLActivity.handleNativeState();
     }
 
     // Key events