Fixed bug 4580 - Android 8: immersive fullscreen notification causes flickering between fullscreen and non-fullscreen and app is unresponsive
authorSam Lantinga <slouken@libsdl.org>
Mon, 22 Apr 2019 16:19:52 -0700
changeset 12709d268ce129edb
parent 12708 f6193f829c8a
child 12710 6c510e7c6884
Fixed bug 4580 - Android 8: immersive fullscreen notification causes flickering between fullscreen and non-fullscreen and app is unresponsive

Sylvain 2019-04-18 21:22:59 UTC

Changes:
- SDL_WINDOWEVENT_FOCUS_GAINED and SDL_WINDOWEVENT_FOCUS_LOST are sent when the java method onWindowFocusChanged() is called.

- If we have support for MultiWindow (eg API >= 24), SDL event loop is blocked/un-blocked (or simply egl-backed-up or not), when java onStart()/onStop() are called.

- If not, this behaves like now, SDL event loop is blocked/un-blocked when onPause()/onResume() are called.

So if we have two app on screen and switch from one to the other, only FOCUS events are sent (and onPause()/onResume() are called but empty. onStart()/onStop() are not called).
The SDL app, un-focused, would still continue to run and display frames (currently the App would be displayed, but paused).
Like a video player app or a chronometer that would still be refreshed, even if the window hasn't the focus.
It should work also on ChromeBooks (not tested), with two apps opened at the same time.


I am not sure this fix Dan's issue. Because focus lost event triggers Minimize function (which BTW is not provided on android).
https://hg.libsdl.org/SDL/file/8703488687ca/src/video/SDL_video.c#l2653
https://hg.libsdl.org/SDL/file/8703488687ca/src/video/SDL_video.c#l2634

So, in addition, it would need to add by default SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS to 0.
So that the lost focus event doesn't try to minimize the window. And this should fix also the issue.
android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
src/core/android/SDL_android.c
     1.1 --- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java	Sun Apr 21 21:34:14 2019 -0400
     1.2 +++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java	Mon Apr 22 16:19:52 2019 -0700
     1.3 @@ -38,6 +38,7 @@
     1.4      private static final String TAG = "SDL";
     1.5  
     1.6      public static boolean mIsResumedCalled, mHasFocus;
     1.7 +    public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24);
     1.8  
     1.9      // Cursor types
    1.10      private static final int SDL_SYSTEM_CURSOR_NONE = -1;
    1.11 @@ -274,16 +275,12 @@
    1.12          }
    1.13      }
    1.14  
    1.15 -    // Events
    1.16 -    @Override
    1.17 -    protected void onPause() {
    1.18 -        Log.v(TAG, "onPause()");
    1.19 -        super.onPause();
    1.20 +    protected void pauseNativeThread() {
    1.21          mNextNativeState = NativeState.PAUSED;
    1.22          mIsResumedCalled = false;
    1.23  
    1.24          if (SDLActivity.mBrokenLibraries) {
    1.25 -           return;
    1.26 +            return;
    1.27          }
    1.28  
    1.29          if (mHIDDeviceManager != null) {
    1.30 @@ -293,10 +290,7 @@
    1.31          SDLActivity.handleNativeState();
    1.32      }
    1.33  
    1.34 -    @Override
    1.35 -    protected void onResume() {
    1.36 -        Log.v(TAG, "onResume()");
    1.37 -        super.onResume();
    1.38 +    protected void resumeNativeThread() {
    1.39          mNextNativeState = NativeState.RESUMED;
    1.40          mIsResumedCalled = true;
    1.41  
    1.42 @@ -311,6 +305,43 @@
    1.43          SDLActivity.handleNativeState();
    1.44      }
    1.45  
    1.46 +    // Events
    1.47 +    @Override
    1.48 +    protected void onPause() {
    1.49 +        Log.v(TAG, "onPause()");
    1.50 +        super.onPause();
    1.51 +        if (!mHasMultiWindow) {
    1.52 +            pauseNativeThread();
    1.53 +        }
    1.54 +    }
    1.55 +
    1.56 +    @Override
    1.57 +    protected void onResume() {
    1.58 +        Log.v(TAG, "onResume()");
    1.59 +        super.onResume();
    1.60 +        if (!mHasMultiWindow) {
    1.61 +            resumeNativeThread();
    1.62 +        }
    1.63 +    }
    1.64 +
    1.65 +    @Override
    1.66 +    protected void onStop() {
    1.67 +        Log.v(TAG, "onStop()");
    1.68 +        super.onStop();
    1.69 +        if (mHasMultiWindow) {
    1.70 +            pauseNativeThread();
    1.71 +        }
    1.72 +    }
    1.73 +
    1.74 +    @Override
    1.75 +    protected void onStart() {
    1.76 +        Log.v(TAG, "onStart()");
    1.77 +        super.onStart();
    1.78 +        if (mHasMultiWindow) {
    1.79 +            resumeNativeThread();
    1.80 +        }
    1.81 +    }
    1.82 +
    1.83      public static int getCurrentOrientation() {
    1.84          final Context context = SDLActivity.getContext();
    1.85          final Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
    1.86 @@ -347,15 +378,21 @@
    1.87             return;
    1.88          }
    1.89  
    1.90 -        SDLActivity.mHasFocus = hasFocus;
    1.91 +        mHasFocus = hasFocus;
    1.92          if (hasFocus) {
    1.93             mNextNativeState = NativeState.RESUMED;
    1.94             SDLActivity.getMotionListener().reclaimRelativeMouseModeIfNeeded();
    1.95 +
    1.96 +           SDLActivity.handleNativeState();
    1.97 +           nativeFocusChanged(true);
    1.98 +
    1.99          } else {
   1.100 -           mNextNativeState = NativeState.PAUSED;
   1.101 +           nativeFocusChanged(false);
   1.102 +           if (!mHasMultiWindow) {
   1.103 +               mNextNativeState = NativeState.PAUSED;
   1.104 +               SDLActivity.handleNativeState();
   1.105 +           }
   1.106          }
   1.107 -
   1.108 -        SDLActivity.handleNativeState();
   1.109      }
   1.110  
   1.111      @Override
   1.112 @@ -719,6 +756,7 @@
   1.113      public static native void nativeQuit();
   1.114      public static native void nativePause();
   1.115      public static native void nativeResume();
   1.116 +    public static native void nativeFocusChanged(boolean hasFocus);
   1.117      public static native void onNativeDropFile(String filename);
   1.118      public static native void nativeSetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, int format, float rate);
   1.119      public static native void onNativeResize();
     2.1 --- a/src/core/android/SDL_android.c	Sun Apr 21 21:34:14 2019 -0400
     2.2 +++ b/src/core/android/SDL_android.c	Mon Apr 22 16:19:52 2019 -0700
     2.3 @@ -131,6 +131,9 @@
     2.4  JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
     2.5          JNIEnv *env, jclass cls);
     2.6  
     2.7 +JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)(
     2.8 +        JNIEnv *env, jclass cls, jboolean hasFocus);
     2.9 +
    2.10  JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
    2.11          JNIEnv *env, jclass cls,
    2.12          jstring name);
    2.13 @@ -1034,7 +1037,6 @@
    2.14      __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
    2.15  
    2.16      if (Android_Window) {
    2.17 -        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
    2.18          SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
    2.19          SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
    2.20          SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
    2.21 @@ -1060,7 +1062,6 @@
    2.22      if (Android_Window) {
    2.23          SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
    2.24          SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
    2.25 -        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
    2.26          SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0);
    2.27      }
    2.28  
    2.29 @@ -1073,6 +1074,19 @@
    2.30      SDL_UnlockMutex(Android_ActivityMutex);
    2.31  }
    2.32  
    2.33 +JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)(
    2.34 +                                    JNIEnv *env, jclass cls, jboolean hasFocus)
    2.35 +{
    2.36 +    SDL_LockMutex(Android_ActivityMutex);
    2.37 +
    2.38 +    if (Android_Window) {
    2.39 +        __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeFocusChanged()");
    2.40 +        SDL_SendWindowEvent(Android_Window, (hasFocus ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST), 0, 0);
    2.41 +    } 
    2.42 +
    2.43 +    SDL_UnlockMutex(Android_ActivityMutex);
    2.44 +}
    2.45 +
    2.46  JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
    2.47                                      JNIEnv *env, jclass cls,
    2.48                                      jstring text, jint newCursorPosition)