Skip to content
Permalink
Browse files

android: More robust fix for screen locking in landscape (thanks, Syl…

…vain!).

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."
  • Loading branch information
icculus committed Apr 8, 2017
1 parent 27023ed commit 366adae360796a7a8a265a78626e37cbb905c37f
Showing with 97 additions and 61 deletions.
  1. +97 −61 android-project/src/org/libsdl/app/SDLActivity.java
@@ -36,8 +36,16 @@
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 static void initialize() {
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 void onClick(DialogInterface dialog,int id) {
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 void onWindowFocusChanged(boolean hasFocus) {

SDLActivity.mHasFocus = hasFocus;
if (hasFocus) {
SDLActivity.handleResume();
mNextNativeState = NativeState.RESUMED;
} else {
mNextNativeState = NativeState.PAUSED;
}

SDLActivity.handleNativeState();
}

@Override
@@ -247,6 +265,9 @@ public void onLowMemory() {
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 boolean dispatchKeyEvent(KeyEvent event) {
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 @@ public void surfaceCreated(SurfaceHolder holder) {
@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 @@ else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || req

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

0 comments on commit 366adae

Please sign in to comment.