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."
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
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