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."
This commit is contained in:
Ryan C. Gordon 2017-04-07 20:17:30 -04:00
parent 27023ed2b3
commit 366adae360
1 changed files with 98 additions and 62 deletions

View File

@ -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() {
/** 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();
if (mNextNativeState == mCurrentNativeState) {
// Already in same state, discard.
return;
}
// 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;
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
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 (SDLActivity.mHasFocus && !SDLActivity.mIsPaused) {
SDLActivity.handleResume();
}
SDLActivity.handleNativeState();
}
// Key events