Maintain Immersive mode when DialogFragment is Shown
Asked Answered
Q

5

10

I have an Android Application that is made using Fragments

I am hiding the bars at top and bottom of my screen, using the following code.

 @Override
protected void onResume() {
    super.onResume();
    isInBackground = false;

    if(null == getFragmentManager().findFragmentById(R.id.content_container))
    {
        getFragmentManager().beginTransaction().add(R.id.content_container,new PresenterFragment(), PresenterFragment.FRAG_TAG).commit();
    }

    if(Build.VERSION.SDK_INT >=19)
    {
        View decorView = getWindow().getDecorView();
        int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
                | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
        decorView.setSystemUiVisibility(uiOptions);
        decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
            @Override
            public void onSystemUiVisibilityChange(int visibility) {
                View decorView = getWindow().getDecorView();
                int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
                        | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
                decorView.setSystemUiVisibility(uiOptions);
            }
        });
    }
}

When the soft keyboard it shown the bars show, I can live with this as they hide when the keyboard is dismissed.However if a dialog Fragment is show while the soft keyboard is shown then when both the keyboard and the dialog fragment are dismissed they bars remain over the top of the application.

My 3 questions are

Is it possible to stop the softkeyboard for changing the UI mode?

Is it possible to stop the showing of DialogsFraments from changing the UI mode?

edit: I used to below code to see if the keyboard is shown

public static boolean isKeyBoardShown(){
    InputMethodManager imm = (InputMethodManager)currentActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
    if (imm.isAcceptingText()) {
        return true;
    } else {
        return false;
    }
}

-> I know that there is a work around for dialogs in an activity but I can't see one or rework the code to work in a DialogFragment

If neither is possible why does the app get stuck in the wrong UI mode when there is both a shown keyboard and DialogFrament?

Queue answered 24/9, 2015 at 10:2 Comment(0)
P
33

1. Explanation for solution.

I have taken the following quotes from the android api docs.

Using Immersive Full-Screen Mode

When immersive full-screen mode is enabled, your activity continues to receive all touch events. The user can reveal the system bars with an inward swipe along the region where the system bars normally appear. This clears the SYSTEM_UI_FLAG_HIDE_NAVIGATION flag (and the SYSTEM_UI_FLAG_FULLSCREEN flag, if applied) so the system bars become visible. This also triggers your View.OnSystemUiVisibilityChangeListener, if set.

Firstly, you don't need an OnSystemUiVisibilityChangeListener when using sticky immersion.

However, if you'd like the system bars to automatically hide again after a few moments, you can instead use the SYSTEM_UI_FLAG_IMMERSIVE_STICKY flag. Note that the "sticky" version of the flag doesn't trigger any listeners, as system bars temporarily shown in this mode are in a transient state.

The recommendations for using sticky/non sticky immersion:

If you're building a truly immersive app, where you expect users to interact near the edges of the screen and you don't expect them to need frequent access to the system UI, use the IMMERSIVE_STICKY flag in conjunction with SYSTEM_UI_FLAG_FULLSCREEN and SYSTEM_UI_FLAG_HIDE_NAVIGATION. For example, this approach might be suitable for a game or a drawing app.

However, you mention users needing the keyboard, so I suggest this:
Use Non-Sticky Immersion

If you're building a book reader, news reader, or a magazine, use the IMMERSIVE flag in conjunction with SYSTEM_UI_FLAG_FULLSCREEN and SYSTEM_UI_FLAG_HIDE_NAVIGATION. Because users may want to access the action bar and other UI controls somewhat frequently, but not be bothered with any UI elements while flipping through content, IMMERSIVE is a good option for this use case.


2. Solution

My solution is to set up your view ui in the onActivityCreated of your fragments.

My example taken from ImmersiveModeFragment.java sample.

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    final View decorView = getActivity().getWindow().getDecorView();
    decorView.setOnSystemUiVisibilityChangeListener(
            new View.OnSystemUiVisibilityChangeListener() {
                @Override
                public void onSystemUiVisibilityChange(int i) {
                    hideSystemUI();
                }
            });
}

Create a separate method to manage the ui that you call from your OnSystemUiVisibilityChangeListener()

Taken from here non sticky immersion

private void hideSystemUI() {
    // Set the IMMERSIVE flag.
    // Set the content to appear under the system bars so that the content
    // doesn't resize when the system bars hide and show.
    mDecorView.setSystemUiVisibility(
        View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
        | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
        | View.SYSTEM_UI_FLAG_IMMERSIVE);
}

You can then call this method again onResume.

onResume(){
    hideSystemUI();
}

3. Alternate solution.

If sticky immersion is what you really want you need to try a different approach.

For sticky immersion

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus) {
        decorView.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_FULLSCREEN
            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);}
}

I hope this solves your problems.

Pilewort answered 3/10, 2015 at 16:57 Comment(3)
Your alternate solution solved the issue that I was struggling with for several hours. Thank you!Lavern
I added the options for sticky immersion put for some reason the app still shows the navigation bar and status bar for a brief period of time when I open the dialog frame. It's as if the layout for the status and navigation bars was present but not visible, and then when the dialog frame appears they become visible but are hidden (their views are moved out of the screen). Any idea what could be causing the issue?Bonaparte
@Bonaparte I've tried to add the next parameters inside my app theme style: <item name="android:windowNoTitle">true</item> <item name="android:windowFullscreen">true</item> And it helped but only with status bar. The bottom bar is still shows for a brief period of time. Maybe have some ideas here?Onesided
F
5

I know this is old but I was struggling with this for a while as I had dialogFragments and a drop down in my navigationView that kept revealing the system UI needlessly. Here are the things I did that ended up working (the first two pieces I found a lot of places):

In my AndroidManifest.xml on the application

android:theme="@style/AppTheme.NoActionBar"

In my activity

View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
decorView.setSystemUiVisibility(uiOptions);

And what ended up being the key for me is in my styles.xml

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowFullscreen">true</item>
    </style>
</resources>
Friendly answered 12/1, 2019 at 7:41 Comment(0)
T
2

Is it possible to stop the softkeyboard for changing the UI mode?

Maybe

Is it possible to stop the showing of DialogsFraments from changing the UI mode?

Yes if only you try to understand what you are doing

If neither is possible why does the app get stuck in the wrong UI mode when there is both a shown keyboard and DialogFrament?

because of the answer on question 2

now this is your overall solution

The current/Focused View is who the os takes UI visibility settings from,when a View is obscured or is not on top in respect to the z-order then setSystemUiVisibility() is set to its default. So instead try hacking around

View decorView = getWindow().getCurrentFocus() != null ? 
                getWindow().getCurrentFocus() :getWindow().getDecorView();

and whenever your DialogFragment is dismissed check for the above line and re-call your accessibility ui codes; because .... guess is lucid to this point

Telemechanics answered 30/9, 2015 at 2:58 Comment(0)
O
2

In Dialog or BottomSheetDialogFragment you have to implement this solution which is work for me.

Step 1:

In your dialog or BottomSheetDialog, write this code in onActivityCreated method,

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
var viewParent = view
while (viewParent is View) {
    viewParent.fitsSystemWindows = false
    viewParent.setOnApplyWindowInsetsListener { _, insets -> insets }
    viewParent = viewParent.parent as View?
 }
}

Step 2: Also, override the below method :

override fun setupDialog(dialog: Dialog, style: Int) {
super.setupDialog(dialog, style)
dialog?.window?.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)

}

Now see the magic :)

Outage answered 13/1, 2020 at 12:39 Comment(0)
V
1

This fix, plus another fix related to the soft keyboard is included in the DialogFragment class from this library. Just extend this instead of the standard library:

https://github.com/marksalpeter/contract-fragment

The main purposes of the library is to add delegate functionality to Fragments and DialogFragments that leverage the parent/child fragment relationship, but you can just use the bug fix or copy/paste the file

Vivianna answered 14/2, 2018 at 0:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.