| Summary: | Android 8: immersive fullscreen notification causes flickering between fullscreen and non-fullscreen and app is unresponsive | ||
|---|---|---|---|
| Product: | SDL | Reporter: | Dan Ginsburg <dang> |
| Component: | video | Assignee: | Sam Lantinga <slouken> |
| Status: | ASSIGNED --- | QA Contact: | Sam Lantinga <slouken> |
| Severity: | normal | ||
| Priority: | P2 | CC: | dang, etc0de, icculus, sylvain.becker |
| Version: | HG 2.1 | ||
| Hardware: | ARM | ||
| OS: | Android (All) | ||
| Attachments: |
patch
patch 2 (multi-window) patch MinimizeWindow |
||
Sylvain, can you look at this? I think he's correct that onPause() doesn't necessarily mean minimized, especially in multi-window environments like ChromeOS. I'll really try next week I think. But in the meantimes: - how can we simulate/trigger such notifications ? during those notifications, you only Pause/Resume and no Stop/Restart (you have to add and log them in SDLActivity )? Is that working if you comment out "SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);" in src/core/android/SDL_android.c ? (In reply to Sylvain from comment #2) > - how can we simulate/trigger such notifications ? Before launching a fullscreen immersive SDL app, run this command in adb shell: $ settings put secure immersive_mode_confirmations 0 That resets the system setting that determines whether the user has ever confirmed the notification before. > > during those notifications, you only Pause/Resume and no Stop/Restart (you > have to add and log them in SDLActivity )? > That is correct. I added onStop/onRestart logging and this is what the sequence of events looks like as it flickers in and out: 04-05 10:28:34.205 12238 12238 V SDL : Device: G8142 04-05 10:28:34.205 12238 12238 V SDL : Model: G8142 04-05 10:28:34.205 12238 12238 V SDL : onCreate() 04-05 10:28:34.206 12238 12238 V SDL : nativeSetupJNI() 04-05 10:28:34.206 12238 12238 V SDL : AUDIO nativeSetupJNI() 04-05 10:28:34.206 12238 12238 V SDL : CONTROLLER nativeSetupJNI() 04-05 10:28:34.221 12238 12238 V SDL : onResume() 04-05 10:28:34.264 12238 12238 V SDL : surfaceCreated() 04-05 10:28:34.264 12238 12238 V SDL : surfaceChanged() 04-05 10:28:34.264 12238 12238 V SDL : pixel format RGB_565 04-05 10:28:34.265 12238 12238 V SDL : Window size: 1776x1008 04-05 10:28:34.265 12238 12238 V SDL : Device size: 1920x1080 04-05 10:28:34.271 12238 12280 V SDL : Running main function SDL_main from library [removed] 04-05 10:28:34.271 12238 12280 V SDL : nativeRunMain() 04-05 10:28:34.272 12238 12238 V SDL : onWindowFocusChanged(): true 04-05 10:28:35.419 12238 12307 V SDL : setOrientation() orientation=6 width=1 height=1 resizable=false hint=LandscapeLeft LandscapeRight 04-05 10:28:35.431 12238 12238 V SDL : surfaceDestroyed() 04-05 10:28:35.431 12238 12238 V SDL : nativePause() 04-05 10:28:35.435 12238 12238 V SDL : surfaceCreated() 04-05 10:28:35.435 12238 12238 V SDL : surfaceChanged() 04-05 10:28:35.435 12238 12238 V SDL : pixel format RGBA_8888 04-05 10:28:35.436 12238 12238 V SDL : Window size: 1776x1008 04-05 10:28:35.436 12238 12238 V SDL : Device size: 1920x1080 04-05 10:28:35.438 12238 12238 V SDL : nativeResume() 04-05 10:28:35.458 12238 12280 V SDL : setOrientation() orientation=6 width=1920 height=1080 resizable=false hint=LandscapeLeft LandscapeRight 04-05 10:28:35.504 12238 12238 V SDL : surfaceChanged() 04-05 10:28:35.504 12238 12238 V SDL : pixel format RGBA_8888 04-05 10:28:35.504 12238 12238 V SDL : Window size: 1920x1080 04-05 10:28:35.504 12238 12238 V SDL : Device size: 1920x1080 04-05 10:28:36.313 12238 12238 V SDL : onWindowFocusChanged(): false 04-05 10:28:36.313 12238 12238 V SDL : nativePause() 04-05 10:28:36.890 12238 12238 V SDL : surfaceChanged() 04-05 10:28:36.890 12238 12238 V SDL : pixel format RGBA_8888 04-05 10:28:36.891 12238 12238 V SDL : Window size: 1776x1008 04-05 10:28:36.891 12238 12238 V SDL : Device size: 1920x1080 04-05 10:28:36.899 12238 12238 V SDL : onWindowFocusChanged(): true 04-05 10:28:36.899 12238 12238 V SDL : nativeResume() 04-05 10:28:37.468 12238 12238 V SDL : surfaceChanged() 04-05 10:28:37.468 12238 12238 V SDL : pixel format RGBA_8888 04-05 10:28:37.469 12238 12238 V SDL : Window size: 1920x1080 04-05 10:28:37.469 12238 12238 V SDL : Device size: 1920x1080 04-05 10:28:38.258 12238 12238 V SDL : onWindowFocusChanged(): false 04-05 10:28:38.258 12238 12238 V SDL : nativePause() 04-05 10:28:38.839 12238 12238 V SDL : surfaceChanged() 04-05 10:28:38.839 12238 12238 V SDL : pixel format RGBA_8888 04-05 10:28:38.840 12238 12238 V SDL : Window size: 1776x1008 04-05 10:28:38.840 12238 12238 V SDL : Device size: 1920x1080 04-05 10:28:38.851 12238 12238 V SDL : onWindowFocusChanged(): true 04-05 10:28:38.851 12238 12238 V SDL : nativeResume() 04-05 10:28:38.920 12238 12280 V SDL : SDL audio: opening device for output 04-05 10:28:39.417 12238 12238 V SDL : surfaceChanged() 04-05 10:28:39.417 12238 12238 V SDL : pixel format RGBA_8888 04-05 10:28:39.417 12238 12238 V SDL : Window size: 1920x1080 04-05 10:28:39.417 12238 12238 V SDL : Device size: 1920x1080 04-05 10:28:40.189 12238 12238 V SDL : onWindowFocusChanged(): false 04-05 10:28:40.189 12238 12238 V SDL : nativePause() 04-05 10:28:40.754 12238 12238 V SDL : surfaceChanged() 04-05 10:28:40.754 12238 12238 V SDL : pixel format RGBA_8888 04-05 10:28:40.755 12238 12238 V SDL : Window size: 1776x1008 04-05 10:28:40.755 12238 12238 V SDL : Device size: 1920x1080 04-05 10:28:40.764 12238 12238 V SDL : onWindowFocusChanged(): true 04-05 10:28:40.764 12238 12238 V SDL : nativeResume() 04-05 10:28:41.348 12238 12238 V SDL : surfaceChanged() 04-05 10:28:41.348 12238 12238 V SDL : pixel format RGBA_8888 04-05 10:28:41.349 12238 12238 V SDL : Window size: 1920x1080 04-05 10:28:41.349 12238 12238 V SDL : Device size: 1920x1080 04-05 10:28:42.133 12238 12238 V SDL : onWindowFocusChanged(): false 04-05 10:28:42.133 12238 12238 V SDL : nativePause() When I finally exit the app, I do see my message: 04-05 10:28:47.972 12238 12238 V SDL : surfaceChanged() 04-05 10:28:47.972 12238 12238 V SDL : pixel format RGBA_8888 04-05 10:28:47.973 12238 12238 V SDL : Window size: 1080x1704 04-05 10:28:47.973 12238 12238 V SDL : Device size: 1080x1920 04-05 10:28:47.974 12238 12238 V SDL : Skip .. Surface is not ready. 04-05 10:28:47.984 12238 12238 V SDL : onWindowFocusChanged(): false 04-05 10:28:47.998 12238 12238 V SDL : surfaceDestroyed() 04-05 10:28:48.078 12238 12238 V SDL : onStop() > Is that working if you comment out "SDL_SendWindowEvent(Android_Window, > SDL_WINDOWEVENT_MINIMIZED, 0, 0);" in src/core/android/SDL_android.c ? Not entirely, no. If I do this, it still sets the UI visbible (so exits fullscreen) which seems to clear the notifcation but it never goes back to fullscreen, so this is not the desired behavior. If I remove the SDL_WINDOWEVENT_FOCUS_LOST as well then it does seem to behave as I would expect (the fullscreen message stays there and the UI never becomes invisible). Yes SDL_WINDOWEVENT_FOCUS_LOST event doesn't do much. But SDL_WINDOWEVENT_MINIMIZED, call: SDL_OnWindowMinimized() SDL_UpdateFullscreenMode() _this->SetWindowFullscreen() Android_SetWindowFullscreen() Android_JNI_SetWindowStyle() and java: setWindowStyle() and COMMAND_CHANGE_WINDOW_STYLE In activity lifecyle: https://developer.android.com/guide/components/activities/activity-lifecycle There is onPause -> onResume() that we already use and also onStop() -> onRestart() -> onStart() we can use In: https://developer.android.com/reference/android/opengl/GLSurfaceView Activity Life-cycle A GLSurfaceView must be notified when to pause and resume rendering. GLSurfaceView clients are required to call onPause() when the activity stops and onResume() when the activity starts. These calls allow GLSurfaceView to pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate the OpenGL display. So we still should block and backup egl in onPause()/onResume(). But maybe we can move the Minimized to onStart/onStop() e.g. move SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0); in (future) nativeStart() / nativeRestart() move SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); in (future) nativeStop() I'll try a patch Created attachment 3731 [details]
patch
Here's a patch to move focus lost/gain, and maximize/minized events into onRestart/onStop().
I don't know if this going to solve the issue, but this is what you test as you said.
I can't reproduce the issue with my Samsung s7 Android8 phone.
adb shell settings put secure immersive_mode_confirmations 0
I see no notification :(
I tried to use targetSdk 26 or 28 ... No sure how to get this.
The thing I see with the patch is that we sent the event when the app event loop is already paused.
so the app will recevied the events when it goes to foreground ... not sure if this can hurt.
Also the order of the event seem wrong (I didn't touch it, but maybe this could tried)
on Pause, it sends:
- SDL_WINDOWEVENT_FOCUS_LOST
- SDL_WINDOWEVENT_MINIMIZED
on Resume, it sends:
- SDL_WINDOWEVENT_FOCUS_GAINED
- SDL_WINDOWEVENT_RESTORED
I think that should be:
on Pause, it sends:
- same
on Resume, it sends:
- SDL_WINDOWEVENT_RESTORED
- SDL_WINDOWEVENT_FOCUS_GAINED
Very likely not important ..
Just wondering, what about setting: SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); (In reply to Sylvain from comment #5) > Created attachment 3731 [details] > patch > > Here's a patch to move focus lost/gain, and maximize/minized events into > onRestart/onStop(). I can confirm that your patch fixes the issue on my Sony Xperia XZ (Android 8). The patch looks good to me, can you commit it? > Just wondering, what about setting: > SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); That did not do anything to fix the symptoms of the bug, but your patch fixed it. Thanks! You're right, I think the order of the events on restore should be reversed. Can you confirm that's the order on Windows? I think this patch is the right approach, do you see any regressions with other testing? Is it still sending minimized even though the window is still visible? Because I would have assumed after a minimize event that the window is no longer visible -> drawing can be suspended for perf reasons. So to me it seems wrong that minimize/restore is send in this case at all Oh right, I missed that part mentioning the hint to control that behavior, my bad! Ok sorry to barge in seconds later, but after reading the description of SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS it sounds like either 1. SDL2 should not minimize and not send a minimize event with SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS=0, or 2. actually minimize (= window is no longer visible) and send a minimize event SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS=1. So a situation where I get minimize and the window is still visible still doesn't make too much sense to me (sorry if that never happens and I just got that incorrectly from the comments above) Created attachment 3752 [details] patch 2 (multi-window) Sam, on Windows 10, it seems also to have the wrong order: https://hg.libsdl.org/SDL/annotate/c3e9a2b93517/src/core/winrt/SDL_winrtapp_direct3d.cpp#l466 I am not really happy with the previous patch. Here's a different patch that handles or not multi-windows. As said in https://developer.android.com/guide/topics/ui/multi-window, it exists for API >= 24. And also, it sends Focus events more appropriately. 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. IMHO SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS should default to 1 on all platforms, and if you think about it if you tab out of the app normally (which is the best equivalent to ALT+TAB on desktop) then this is also the behavior you get: you lose focus out of fullscreen, it minimizes. Switching to side by side enabled out of fullscreen on the other hand, that's simply is an operation that no current desktop environment currently offers. So to me it seems like it's just entirely out of the scope of what the hint was supposed to cover, and it's fair to just ignore it for that. Additionally, I think SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS=1 should mean "attempt to minimize, and only send minimize events if it actually succeeds", not "send minimize event and then also attempt to minimize which may not be applicable" (which is what it feels like on Android in side by side if you send minimize). Summed up, I think SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS=1 is an okay default everywhere, it just shouldn't have an influence on side by side But the default for any environment, when you have two open windows and you switch from one to the one, is that you only switch focus. It never minimizes, isn't it? So that should be SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS = 0 Yes, but on home-button-leave-app it will ALWAYS minimize like SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS = 1. (While side by side it never will, alike to SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS = 0) My point is the hint is really meaningless for Android right now, so why switch the default value if it doesn't match what is happening in either case anyway - I would suggest it should just be ignored on Android I think. "home button" triggers onWindowFocusChanged() -> lost focus, and then, it also triggers both onPause() and onStop() -> Minimized. So we doesn't need any SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS in that case. It will get minimized anyway. The default value is SDL_TRUE for SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS. Which seems to me problematic on Android with apps in multi-window/side-by-side. Because, in that case, losing the focus does trigger minimize. (which wouldn't happen without the feature). And it doesn't not actually minimize, it only pauses the event loop. > The default value is SDL_TRUE for SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS.
> Which seems to me problematic on Android with apps in multi-window/side-by-side.
> Because, in that case, losing the focus does trigger minimize. (which wouldn't happen without the feature).
Ok, let me be more clear then: I would consider it problematic if you now changed the default to 0 (that part is fine although I think semantically it's kinda useless) but now if someone set it to 1, SDL2 would again send minimize events it side by side even though nothing was minimized. (that I think should be considered a bug/error-inviting nonsense)
So that is what I mean by changing the default seems useless to me. I just don't think it does anything for the actual issue of "if the window didn't minimize SDL2 shouldn't ever send a minimize event" which I think should be addressed
This is problematic because: - if losing focus triggers minimize, it also produces the issue of the current ticket e.g. toggling the status-bar. - if losing focus triggers the pause, it blocks the apps and the rendering. We don't really want that, because the app is visible: it may have something to display. Blocking the apps is only for saving battery, when the app is really not used in background. Changing the default seems important for me. Because if you don't set the hint, you have the fact that "losing focus triggers minimize". Right, I'm with you there in terms of behavior! Nothing other than not ever attempting this "minimize" makes sense. Just let me describe what specific scenario I'm kind of afraid of: I think some devs may work mainly on desktop and set SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS=1 without being aware of this, and it appears to work fine on all platforms and at a first glance on android, and suddenly side by side is broken for them but they never realize. Do you get what I mean? Because on desktop there is just no equivalent for this where this would ever be a problem, and for the full dedicated fullscreen it DOES make sense to set it to 1. This is why I keep suggesting that the android code should just be HARDCODED to pretend SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS=0 always for side by side, no matter what the hint says. And then it becomes kind of irrelevant what the default value even is, and SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS=1 could stay for all platforms for consistency But of course I agree if you just change the default it will also work for most, and I am starting to repeat myself so I will bow out! Just for the sake of preventing developers from their own stumbling, I think what I propose would make sense. But in the end of course it's up to you! (In reply to Sylvain from comment #12) > 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. I can confirm that your latest patch fixes the issue as long as I also set SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS to 0. It also happens to fix another bug we had which was that we were getting SDL_WINDOWEVENT_FOCUS_LOST but never SDL_WINDOWEVENT_FOCUS_GAINED during startup so our app would think we did not have focus until the app was backgrounded and brought back to the foreground. So it seems to fix two bugs in one shot for us. Btw, another thing occurred to me: Android 10 apparently has an experimental desktop mode now, which may actually offer going fullscreen and tabbing out and a subsequent actual distinction of whether to minimize or not. It would only be natural if SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS controlled behavior in that situation. However, if you tie SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS to side by side working properly or sending a bogus nonsensical minimize, then once that desktop mode actually offers this distinction the hint can no longer be used freely to configure that desktop mode situation. I hope this additional piece of info helps you rethink whether SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS=1 should really break side by side / whether side by side operating properly should really depend on SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS=0 Thanks Sylvain, I pushed your second patch: https://hg.libsdl.org/SDL/rev/d268ce129edb I also made a change so we don't leave fullscreen mode if we can't actually minimize on this platform: https://hg.libsdl.org/SDL/rev/6c510e7c6884 https://hg.libsdl.org/SDL/rev/60b397c49d36 Thanks everyone! Ok! Great!
I've just thinking about it again and have noticed:
- In Android, we are always in *fullscreen*.
- Maybe we can provide the MinimizeWindow implementation for Android.
Intent startMain = new Intent(Intent.ACTION_MAIN);
startMain.addCategory(Intent.CATEGORY_HOME);
startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startMain);
or
this.moveTaskToBack(true);
https://stackoverflow.com/questions/28162815/i-need-to-minimize-the-android-application-on-back-button-click
@ always fullscreen: for what it's worth/just to make sure it's not missed, SDL_SetWindowFullScreen() and the window creation flag for fullscreen do toggle the visibility of the status bar, so SDL2 does appear to have some notion of windowed vs fullscreen (which android calls immersive) mode right now, so that seems to me to be different to a notion of "always fullscreen". Whether side by side should or will leave immersive fullscreen to go to windowed is something I don't know though (also for what it's worth as elaborated above, IMHO side by side really should never trigger a minimize, not even with SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS=1 - even if that would actually be possible) I agree with Jonas, side by side mode shouldn't minimize the app. And when not in side by side mode, putting the task in the background is already minimized, so I think this only becomes relevant in the new Android desktop mode. Thoughts? Also agreed, There is a isInMultiWindowMode() method https://developer.android.com/reference/android/app/Activity.html#isInMultiWindowMode() I'll make a patch: - to add the android MinimizeWindow(), and discard it in multi-window. (same is done for cocoa: https://hg.libsdl.org/SDL/file/9b7633bd0aa0/src/video/SDL_video.c#l2648 ) Not sure how it will behave on ChromeOS or DeX .. I think some notifications could also make the app lose focus ?! and so minimize it ? Created attachment 3762 [details]
patch MinimizeWindow
Here's a patch to minimize window on Android.
And also not to do it in multi-window (nor in picture in picture).
Only tested in multi-window:
with FULLSCREEN window flag, if you change focus, it would minimize the app.
and with test of isInMultiWindowMode(), it doesn't do it. (so it's no op).
But maybe it has some application on ChromeOs, Dex, etc.
We don't want to minimize on focus loss ever, on Android, unless we're in a desktop environment. Right? If we're multi-window, we want to stay visible, if the settings overlay pops up, we want to stay visible, if we're actually task switched away, we'll be automatically minimized... > We don't want to minimize on focus loss ever, on Android, unless we're in a desktop environment. Right?
Not sure whether I'm meant to answer, but from all I've seen I believe that's true. Android 10's experimental desktop UI (which sadly I cannot test due to lack of Android 10 device) is the only situation where I can imagine a minimize to make any sense at all.
I agree I wouldn't use the minimize on phone mode, but on ChromeOs or Android desktop, it would similar as other desktop os. Sounds good. Can you adjust the patch to reflect that? So I'm re-opening the issue in case this present any interest to be merged. But I am not able to really test it: no chromeos, nor dex, nor android 10 desktop. From the patch, maybe: shouldMinimizeOnFocusLoss() should be changed so that: by default return false, and true when: isChromebook() or isDeXMode() or is-android-desktop ? If shouldMinimizeOnFocusLoss()==true means that tabbing out of fullscreen will minimize, then yes that sounds like a good suggestion to me! However, I don't actually know if any of these desktop modes for android even support fullscreen, or minimizing windows. They are all made for office productivity and not for gaming where fullscreen would be more important, so I'm not sure if they do... (sadly I hadn't had the opportunity to try one of these modes myself either) (In reply to Sylvain from comment #34) > So I'm re-opening the issue in case this present any interest to be merged. > > But I am not able to really test it: no chromeos, nor dex, nor android 10 > desktop. I'm inclined to say merge it and see if anyone complains. --ryan. I have added MinimizeWindow() as requested in bug 4657 but shouldMinimizeOnFocusLoss() remains false for android https://hg.libsdl.org/SDL/annotate/f7629f5761d8/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java#l867 Great, I can test this on DeX and enable shouldMinimizeOnFocusLoss() for testing. |
I am testing an SDL app on Android 8 (Sony Xperia XZ Premium) and ran into a bug that happens when the System.Settings.IMMERSIVE_MODE_CONFIRMATIONS comes up. Basically, when that notification comes up, the app is toggled out of fullscreen making the system UI visible. Shortly thereafter, the app toggles itself back to fullscreen hiding the system UI. Here's my triaging of the bug. To review, on Android there is a security setting for immersive fullscreen apps that forces the user to acknowledge this message once. It can be reset in adb shell with: $ settings put secure immersive_mode_confirmations 0 What is happening is that in SDLActivity.java it is constantly getting messages toggling it in and out of fullscreen state. This causes the app to show/hide the system menu on each toggle: case COMMAND_CHANGE_WINDOW_STYLE: if (Build.VERSION.SDK_INT < 19) { // This version of Android doesn't support the immersive fullscreen mode break; } if (context instanceof Activity) { Window window = ((Activity) context).getWindow(); if (window != null) { if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) { int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE; window.getDecorView().setSystemUiVisibility(flags); window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); SDLActivity.mFullscreenModeActive = true; } else { int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE; window.getDecorView().setSystemUiVisibility(flags); window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); SDLActivity.mFullscreenModeActive = false; } } } else { Log.e(TAG, "error handling message, getContext() returned no Activity"); } break; One thing that fixes the symptoms of the bug is if I remove the code to setSystemUiVisibilityFlags in the else case there. But digging further, I tried to figure out why is it that we’re getting the message to toggle out of fullscreen. The reason is because when that Android fullscreen warning pops up we’re getting an onPause invocation from Android. protected void onPause() { Log.v(TAG, "onPause()"); super.onPause(); mNextNativeState = NativeState.PAUSED; mIsResumedCalled = false; if (SDLActivity.mBrokenLibraries) { return; } if (mHIDDeviceManager != null) { mHIDDeviceManager.setFrozen(true); } SDLActivity.handleNativeState(); } ..which ends up calling nativePause. Back over in the native code, we end up with this callstack which is setting fullscreen = false. libSDL2.so!Android_JNI_SetWindowStyle(SDL_bool fullscreen) Line 1257 C libSDL2.so!Android_SetWindowFullscreen(SDL_VideoDevice * _this, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) Line 122 C libSDL2.so!SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen) Line 1333 C libSDL2.so!SDL_MinimizeWindow_REAL(SDL_Window * window) Line 2219 C > libSDL2.so!SDL_SendWindowEvent(SDL_Window * window, Uint8 windowevent, int data1, int data2) Line 174 C libSDL2.so!Java_org_libsdl_app_SDLActivity_nativePause(JNIEnv * env, jclass cls) Line 1046 C [Unknown/Just-In-Time compiled code] What’s triggering the fullscreen toggle is the SDL_WINDOWEVENT_MINIMIZED message which calls SDL_MinimizeWindow which then toggles the fullscreen state to off. NIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)( JNIEnv *env, jclass cls) { SDL_LockMutex(Android_ActivityMutex); __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()"); if (Android_Window) { SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0); SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND); SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND); } /* *After* sending the relevant events, signal the pause semaphore * so the event loop knows to pause and (optionally) block itself. * Sometimes 2 pauses can be queued (eg pause/resume/pause), so it's * always increased. */ SDL_SemPost(Android_PauseSem); SDL_UnlockMutex(Android_ActivityMutex); } One possible idea I have is that we are misinterpreting onPause to mean something that it doesn't. From what I've read, there is a difference between onPause and onStop. In the case of this message, we get an onPause to notify us that we no longer have focus, but we are not minimized. In the case we would be minimized, we would also get onStop. I wonder if maybe a solution here is to only send SDL_WINDOWEVENT_FOCUS_LOST in onPause and have a separate nativeStop that sends the SDL_WINDOWEVENT_MINIMIZED?