15

My app uses the new "immersive mode" by calling (in onCreate):

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);

This works great, but the "how-to" popup ("Swipe down from the top to exit full screen") appears every time the Activity is launched (if the phone is being locked while the activity was showing), even though the user has acknowledged the popup. As far as I understand, the popup is automatically generated by the system, so there's nothing I can do to change this situation, correct?

This issue is reproducible as follows:

  1. Launch immersive Activity [no popup appears, only on the very first launch (correctly)]
  2. Press the power button to switch off screen while the activity is showing
  3. Press power button again to switch on screen
  4. Close Activity by calling finish() e.g. from a button or menu option
  5. Launch Activity again - popup reappears

The popup does NOT reappear if the activity is launched, closed, and relaunched without hitting the power-button in between. Also, it ONLY reappears if the activity was topmost while the power button was pressed.

Correction: The Activity needs to be closed by calling "finish()" (e.g. from a button or a menu option). It works correctly if the Activity is closed by the back-key.

I've uploaded a sample app here: https://github.com/niko001/com.greatbytes.immersivebug/tree/master/Test5

EDIT: There's now an Xposed module to disable the "panic mode", so I guess I'm not alone in seeing this is an annoyance ;)

Nick
  • 3,504
  • 2
  • 39
  • 78
  • Did you mean to use the exclusive or operator (^) rather than bitwise or (|) ? – NickT Nov 19 '13 at 15:50
  • @NickT I copied the code from [here](http://developer.android.com/samples/BasicImmersiveMode/src/com.example.android.basicimmersivemode/BasicImmersiveModeFragment.html). You're right that the ^-operator doesn't make much sense in my case, since it's in `onCreate()`, but I don't think this would make a difference with regard to the "how-to"-popup? – Nick Nov 19 '13 at 16:44
  • 1
    You are probably right, I've not got round to playing with kitkat yet. As it's in a method called toggle..., the XOR makes a bit more sense then. It was just a thought, good luck with it. – NickT Nov 19 '13 at 16:47
  • Sure, thanks anyway, Nick, I appreciate it! – Nick Nov 19 '13 at 20:30

2 Answers2

38

Really interesting question! Thanks to your clear instructions, reproducing the issue wasn't a problem.

Alright, after digging through the source for almost 30-minutes and saying why would they do this? a bunch of times, I think I finally get it. I'll try to explain the best I can, but this is only my interpretation, and may not be correct:

Someone at android realized that the Immersive Mode will send people into a state of panic: how do i exit? (_sorry, I don't know what else the panic would be about_).

In this state of panic, the user will turn to the POWER BUTTON

.... > Power button --> User turns screen off (at x milliseconds since EPOCH)

.... > Praying that the navigation bar comes back

.... > Power button --> User turns screen on (at y milliseconds since EPOCH)

Now, the duration y - x is of significance. We'll discuss it a bit later, but first, let's look at how panic is defined:

panic happens when Praying the navigation bar comes back lasts less than 5 seconds. This value is held by:

mPanicThresholdMs = context.getResources()
                 .getInteger(R.integer.config_immersive_mode_confirmation_panic);

<!-- Threshold (in ms) under which a screen off / screen on will be considered 
     a reset of the immersive mode confirmation prompt.-->
<integer name="config_immersive_mode_confirmation_panic">5000</integer>

Ah, okay. So, it doesn't matter if the user has already acknowledged once, the prompt will be back if the above-mentioned criterion is met - even on the 100th launch.

And here's where the action happens:

public void onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode) {
    if (mPanicPackage != null && !isScreenOn && (time - mPanicTime < mPanicThresholdMs)) {
        // turning the screen back on within the panic threshold
        unconfirmPackage(mPanicPackage);
    }
    if (isScreenOn && inImmersiveMode) {
        // turning the screen off, remember if we were in immersive mode
        mPanicTime = time;
        mPanicPackage = mLastPackage;
    } else {
        mPanicTime = 0;
        mPanicPackage = null;
    }
}

(time - mPanicTime < mPanicThresholdMs) ==> ( y - x ) < 5000

unconfirmPackage(mPanicPackage) removes mPanicPackage (yours) from the list of packages stored under Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS.

Needless to say, I find this strange... and wrong. Even if the user is in panic, and takes the power button route, s/he won't see the helpful reminder until next launch. So, what's the point?

Or may be, I am wrong about the definition of panic.

so there's nothing I can do to change this situation, correct?

Correct. To fix this, you would have to add your package-name to value held by Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS. But, to write to a secure setting, your app requires WRITE_SECURE_SETTINGS permission - not for use by third-party applications.

Links:

ImmersiveModeConfirmation (helper class that manages showing/hiding of confirmation prompt)

Vikram
  • 51,313
  • 11
  • 93
  • 122
  • 2
    Wow, thank you very much for your detailed analysis! I never would have guessed that this is actually a "feature". As you said, it would make sense if the popup reappears *directly* after hitting the power button, not at the next launch (when the user has obviously managed to figure out on his own how to get out of immersive mode). Anyway, thanks again! I'll award the bounty as soon as the cooldown period has passed. – Nick Dec 01 '13 at 13:09
  • 3
    This behavior is actually explained in the Docs: "Reminder bubble—The system displays a reminder bubble the first time users enter immersive mode in your app. The reminder bubble reminds users how to display the system bars. Note: If you want to force the reminder bubble to appear for testing purposes, you can do so by putting the app in immersive mode, turning off the screen with the power button, and then turning the screen back on again within 5 seconds." (source: https://developer.android.com/training/system-ui/immersive.html) – Martin Marconcini Mar 08 '14 at 00:54
  • 1
    What an interesting and bizarrely random approach to solving an imaginary problem; there must be some primo drugs in the Google 'snacks' XD – CCJ Aug 24 '20 at 16:42
  • 1
    For develop on Android 5 (may not useful for most developers nowaday) ,notice that Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS is either "confirmed" or "null", not a package list. – Yeung Mar 09 '21 at 09:27
4

More succinctly - in K, users will see the confirmation when entering immersive mode if:

  • They have not yet confirmed it for that app (package).
  • They "panicked" last time they were in immersive mode. "Panic" in this case means toggling the screen off, then back on in under 5 seconds (by default).
jspurlock
  • 1,466
  • 10
  • 7
  • Thanks for the "official confirmation" :-), John! It makes sense to have a "panic"-mode, but I fail to see it's purpose if the instructions reappear *after* the user has evidently (succesfully) managed to escape the immersive mode? I would understand if the instructions reappeared after the user toggled the screen off/on within 5 seconds, but to me, showing the instructions again on the next app launch doesn't make any sense. But anyway, I'm sure there are good reasons for this, thanks again for the clarification :). – Nick Dec 17 '13 at 17:14
  • After the user turns the screen back on, immersive mode has already been reset, and the user is likely looking at the lockscreen - wouldn't want to show it there. If they unlock, and if the top-most app re-enters immersive mode (see Books for an example), this is a short delay between signal (screen toggle) and response (show confirmation). It's true that this may occur much later if the user goes somewhere else, or if the app does not immediately re-enter immersive mode. – jspurlock Dec 17 '13 at 17:23