Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When using Immersive Mode with dialogs, nav. bar reappears and resizes my layout

I'm using Immersive Mode in my app when it's running on Android 4.4+. (http://developer.android.com/training/system-ui/immersive.html)

My activity indeed shows in full screen, and I work around the volume key pressing by using setOnSystemUiVisibilityChangeListener. I also have similar code for putting dialogs into immersive mode.

However, when a dialog is shown, the nav. bars jump on the screen and then retreat immediately. When the dialog is dismissed it's even worse - the nav. bars jump and resize the activity behind.

The following is my class for supporting immersive mode. It is simply called on each Activity's onResume and also a separate function is called when building each dialog.

Did I miss any flag or callback, or is it a known Android issue?

public class ImmersiveModeHelper {

    public ImmersiveModeHelper(Activity activity)
    {
        mActivity = activity;
    }

    @SuppressLint("NewApi")
    public void supportFullScreenImmersiveMode()
    {
        MyLog.d("ImmersiveModeHelper: supportFullScreenImmersiveMode: ");

        // Support full-screen immersive mode on Android 4.4 and up
        if (Build.VERSION.SDK_INT >= 19)
        {
            // Get the needed flags by reflection and use them
            try
            {
                final int immersiveFlag = View.class.getField("SYSTEM_UI_FLAG_IMMERSIVE_STICKY")
                        .getInt(null);
                final int hideNavigationFlag = View.class
                        .getField("SYSTEM_UI_FLAG_HIDE_NAVIGATION").getInt(null);
                final int fullScreenFlag = View.class.getField("SYSTEM_UI_FLAG_FULLSCREEN").getInt(
                        null);


                // Set the flags to the window decor view
                mActivity.getWindow().getDecorView()
                        .setSystemUiVisibility(immersiveFlag | hideNavigationFlag | fullScreenFlag);

                // Set a callback to be called when visibility changes
                // (workaround
                // for volume keys)
                mActivity
                        .getWindow()
                        .getDecorView()
                        .setOnSystemUiVisibilityChangeListener(
                                new View.OnSystemUiVisibilityChangeListener()
                                {
                                    @Override
                                    public void onSystemUiVisibilityChange(int visibility)
                                    {
                                        MyLog.d("ImmersiveModeHelper.supportFullScreenImmersiveMode().new OnSystemUiVisibilityChangeListener() {...}: onSystemUiVisibilityChange: " +
                                                "");

                                        if ((visibility & (immersiveFlag | hideNavigationFlag)) == 0)
                                        {
                                            Handler uiHandler = UiThreadUtils.getUiHandler();
                                            uiHandler.removeCallbacks(mHideSystemUiCallback);
                                            uiHandler.postDelayed(mHideSystemUiCallback,
                                                    HIDE_SYSTEM_UI_DELAY_MILLI);
                                        }
                                    }
                                });

            } catch (Exception e)
            {
                e.printStackTrace();
                MyLog.e("ImmersiveModeHelper: supportFullScreenImmersiveMode: couldn't support immersive mode by reflection");
            }
        } else
        {
            MyLog.i("ImmersiveModeHelper: supportFullScreenImmersiveMode: not supported on this platform version");
        }
    }

    public static void supportFullScreenImmersiveModeForDialog(final Dialog dlg)
    {
        MyLog.d("ImmersiveModeHelper: supportFullScreenImmersiveModeForDialog: ");

        // Support full-screen immersive mode on Android 4.4 and up
        if (Build.VERSION.SDK_INT >= 19)
        {
            final Window dlgWindow = dlg.getWindow();

            // Get the needed flags by reflection and use them
            try
            {
                final int immersiveFlag = View.class.getField("SYSTEM_UI_FLAG_IMMERSIVE_STICKY")
                        .getInt(null);
                final int hideNavigationFlag = View.class
                        .getField("SYSTEM_UI_FLAG_HIDE_NAVIGATION").getInt(null);
                final int fullScreenFlag = View.class.getField("SYSTEM_UI_FLAG_FULLSCREEN").getInt(
                        null);


                // Set the flags to the window decor view
                int flags = dlgWindow.getDecorView().getSystemUiVisibility();
                flags |= (immersiveFlag | hideNavigationFlag | fullScreenFlag);
                dlgWindow.getDecorView().setSystemUiVisibility(flags);

                // Set a callback to be called when visibility changes
                // (workaround for volume keys)
                dlgWindow.getDecorView().setOnSystemUiVisibilityChangeListener(
                        new View.OnSystemUiVisibilityChangeListener()
                        {
                            @Override
                            public void onSystemUiVisibilityChange(int visibility)
                            {
                                MyLog.d("ImmersiveModeHelper.supportFullScreenImmersiveModeForDialog(...).new OnSystemUiVisibilityChangeListener() {...}: onSystemUiVisibilityChange: ");
                                if ((visibility & (immersiveFlag | hideNavigationFlag)) == 0)
                                {
                                    Runnable hideSystemUiCallback = new Runnable()
                                    {
                                        @Override
                                        public void run()
                                        {
                                            supportFullScreenImmersiveModeForDialog(dlg);
                                        }
                                    };

                                    Handler uiHandler = UiThreadUtils.getUiHandler();
                                    uiHandler.removeCallbacks(hideSystemUiCallback);
                                    uiHandler.postDelayed(hideSystemUiCallback,
                                            HIDE_SYSTEM_UI_DELAY_MILLI);
                                }
                            }
                        });

            } catch (Exception e)
            {
                e.printStackTrace();
                MyLog.e("ImmersiveModeHelper: supportFullScreenImmersiveMode: couldn't support immersive mode by reflection");
            }
        } else
        {
            MyLog.i("ImmersiveModeHelper: supportFullScreenImmersiveMode: not supported on this platform version");
        }
    }

    private Activity mActivity;

    private Runnable mHideSystemUiCallback = new Runnable()
    {
        @Override
        public void run()
        {
            supportFullScreenImmersiveMode();
        }
    };

    private static final int HIDE_SYSTEM_UI_DELAY_MILLI = 0;

}
like image 564
SirKnigget Avatar asked Mar 22 '14 12:03

SirKnigget


2 Answers

From the Google API: It's good practice to include other system UI flags (such as SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION and SYSTEM_UI_FLAG_LAYOUT_STABLE) to keep the content from resizing when the system bars hide and show.

You should also make sure that the action bar and other UI controls are hidden at the same time. This snippet demonstrates how to hide and show the status and navigation bars, without resizing the content:

// This snippet hides the system bars.
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);
}

// This snippet shows the system bars. It does this by removing all the flags
// except for the ones that make the content appear under the system bars.
private void showSystemUI() {
    mDecorView.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}

Hope this helps.

like image 127
user2809845 Avatar answered Nov 12 '22 04:11

user2809845


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 :)

like image 20
khushbu Avatar answered Nov 12 '22 04:11

khushbu