44

Before, to enable immersive fullscreen mode, you'd have to use setSystemUiVisibility, like so:

getWindow().getDecorView().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);

As of API 30, it's deprecated, so what's the workaround?

Lev Leontev
  • 2,538
  • 2
  • 19
  • 31

8 Answers8

61

For compatibility, use WindowCompat and WindowInsetsControllerCompat. You'll need to upgrade your gradle dependency for androidx.core to at least 1.6.0-alpha03 so that there will be support for setSystemBarsBehavior on SDK < 30. mainContainer is the top-level ConstraintLayout in my activity.

private fun hideSystemUI() {
    WindowCompat.setDecorFitsSystemWindows(window, false)
    WindowInsetsControllerCompat(window, mainContainer).let { controller ->
        controller.hide(WindowInsetsCompat.Type.systemBars())
        controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
    }
}

private fun showSystemUI() {
    WindowCompat.setDecorFitsSystemWindows(window, true)
    WindowInsetsControllerCompat(window, mainContainer).show(WindowInsetsCompat.Type.systemBars())
}

You can find out more information about WindowInsets by watching this YouTube video

EDIT:

I didn't consider display cutouts or in-display cameras in this answer previously. In the app theme style, i added the following to display my content above the cutout (to the top of the screen when in portrait mode):

<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>

You can read more at this link: Display Cutout

James
  • 4,573
  • 29
  • 32
  • 4
    I think you mean `1.5.0-alpha05` for the controller compat – avalancha Nov 26 '20 at 14:26
  • @avalancha You're correct, i just checked release notes https://developer.android.com/jetpack/androidx/releases/core I updated the answer thanks – James Nov 27 '20 at 15:11
  • 2
    I found that there may be an issue with setting the `systemBarsBehavior` on some devices using this alpha library. Please see the bug report: https://issuetracker.google.com/issues/173203649#comment2 *Be sure to star issue for more attention – James Dec 05 '20 at 04:04
  • This answer is surely needed in future. Need to keep eye on bug fixes in library. – Chaitanya Karmarkar Dec 23 '20 at 06:46
  • Is there any way to get back value set by `window.setDecorFitsSystemWindows(value)`? Replacement of `(window.decorView.systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0` @James – elementstyle Jan 23 '21 at 15:54
  • Unfortunately, this solution is not working even on Android emulator (API 30). System insets are hidden until I tap the screen for the first time then navigation bar comes back and never leaves in spite of what was set as behaviour. – racs Feb 06 '21 at 06:36
  • @racs did you use the code above or below the *EDIT:*? This is a known bug as mentioned in the post, the library used is still in the alpha stage and the temporary is below the *EDIT:* – James Feb 09 '21 at 15:45
  • @James I used the one without the compat library (so below EDIT), I checked the SDK version myself. – racs Feb 11 '21 at 06:05
  • 1
    In androidx.core Version 1.6.0-alpha03 bug fixes, you can see that this issue is fixed. See its release notes:https://developer.android.com/jetpack/androidx/releases/core. Or see:https://issuetracker.google.com/issues/173203649 – Chaitanya Karmarkar May 11 '21 at 08:49
  • @ChaitanyaKarmarkar You're right! Add support for setSystemBarsBehavior on SDK < 30 in alpha03. Will update my answer. Thank you! – James Jun 10 '21 at 04:21
  • Yes, currently I am using it in my app and so far not faced any problems. – Chaitanya Karmarkar Jun 10 '21 at 12:46
  • 1
    Is `setDecorFitsSystemWindows(false)` really needed? I don't see any difference, except that it [breaks action bar](https://issuetracker.google.com/issues/176380836) on API 29 and below. And, BTW, there is `WindowInsetsCompat.Type.systemBars()` for all bars. – gmk57 Jul 05 '21 at 15:33
  • @gmk57 Good catch with the `systemBars()`, i modified the answer to reflect. I can't answer your first question at the moment. – James Jul 07 '21 at 13:41
  • @James what is the **mainContainer**? – Saurabh Dhage Jul 11 '21 at 15:54
  • @SaurabhDhage Good Question. `mainContainer` is the top-level `ViewGroup` (`ConstraintLayout` in my case) in my activity. I just updated my answer thanks for asking. – James Jul 13 '21 at 17:01
  • Can someone post a Java version of this with compability, Ive been struggling to get it right last couple of hours. – AtomicallyBeyond Sep 21 '21 at 05:18
  • @James couldn't we use `window.decorView` to get the root view rather than passing a `ViewGroup`? This would also simplify this call from Fragments, though. – nuhkoca Sep 30 '21 at 01:30
  • WindowInsetsControllerCompat not supported SDK > 20 – Majid Sadeghi Nov 24 '21 at 10:24
  • You should have voted to close this question, instead of duplicating answer here and https://stackoverflow.com/a/64828067/1650674 only minutes apart. – tir38 Apr 17 '22 at 18:52
32

Here's the answer in Kotlin, in case if you don't want to increase the size of your app by using the androidx library.

private fun setFullscreen() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        window.attributes.layoutInDisplayCutoutMode =
            WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        window.setDecorFitsSystemWindows(false)
        window.insetsController?.apply {
            hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
            systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
        }
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        @Suppress("DEPRECATION")
        window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_FULLSCREEN
                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_IMMERSIVE
                or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
    }
}

Call the function your activity's onCreate method.

ORIGINAL ANSWER:

As documentation suggests, you should use WindowInsetsController.

getWindow().setDecorFitsSystemWindows(false);
WindowInsetsController controller = getWindow().getInsetsController();
if (controller != null) {
    controller.hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
    controller.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
}
Lev Leontev
  • 2,538
  • 2
  • 19
  • 31
  • 3
    ok i tried this, but this requires minimum api level 30. So my question is - "What should be implemented for api levels that are below level 30?" – Mohit Yadav Aug 30 '20 at 17:23
  • 2
    @MohitYadav you usually do it with an if, such as `if (Build.VERSION.SDK_INT < 30) { ... here goes the version for old devices ... } else { here comes the newer version }` – Lev Leontev Aug 30 '20 at 20:40
  • 1
    oh ok. I had implemented in the same way, but was confused if there was new way in jetpack.. Thanks. – Mohit Yadav Aug 31 '20 at 04:23
  • Is there any new way which is compatible with old versions too? – Chaitanya Karmarkar Sep 14 '20 at 05:50
  • Yes, you can make it compatible with older versions, see my answer: https://stackoverflow.com/a/64828028/3422470 – James Dec 08 '20 at 19:15
  • Is there any way to get back value set by `window.setDecorFitsSystemWindows(value)`? Replacement of `(window.decorView.systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0` @LeoLeontev – elementstyle Jan 23 '21 at 15:56
10

For the deprecation, you can try the below code. Worked for me.

  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        final WindowInsetsController controller = getWindow().getInsetsController();

        if (controller != null)
            controller.hide(WindowInsets.Type.statusBars());
    }
    else {
        //noinspection deprecation
                getWindow().getDecorView().setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_IMMERSIVE
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    }
6

This works for me, mixed two answers:

First create method :

private void setFullScreen(){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        getWindow().setDecorFitsSystemWindows(false);
        WindowInsetsController controller = getWindow().getInsetsController();
        if(controller != null) {
            controller.hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
            controller.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
        }
    }
    else {
        //noinspection deprecation
        getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_IMMERSIVE
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    }
}

then on create just call it after setContentView like this :

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_menu);
        setFullScreen();
    }

and it works like a charm :D

Web.11
  • 406
  • 2
  • 8
  • 23
4

For fullscreen

Hide StatusBar

private void hideSystemBars() {
    WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
    WindowInsetsControllerCompat windowInsetsController = ViewCompat.getWindowInsetsController(getWindow().getDecorView());
    if (windowInsetsController == null) {
        return;
    }
    // Configure the behavior of the hidden system bars
    windowInsetsController.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
    // Hide both the status bar and the navigation bar
    windowInsetsController.hide(WindowInsetsCompat.Type.systemBars());
}

show StatusBar

private void showSystemBars() {
    WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
    WindowInsetsControllerCompat windowInsetsController = ViewCompat.getWindowInsetsController(getWindow().getDecorView());
    if (windowInsetsController == null) {
        return;
    }
    // Configure the behavior of the hidden system bars
    windowInsetsController.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
    // Hide both the status bar and the navigation bar
    windowInsetsController.show(WindowInsetsCompat.Type.systemBars());
}

for top camera cutting in full screen mode

<item name="android:windowLayoutInDisplayCutoutMode" tools:ignore="NewApi">shortEdges</item>
Pankaj Talaviya
  • 3,328
  • 28
  • 31
3

I'm using this one, work perfectly for all Android version

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            window.setDecorFitsSystemWindows(false)
            val controller = window.insetsController
            if (controller != null) {
                controller.hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
                controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
            }
        } else {
            // All below using to hide navigation bar
            val currentApiVersion = Build.VERSION.SDK_INT
            val flags = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    or View.SYSTEM_UI_FLAG_FULLSCREEN
                    or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)

            // This work only for android 4.4+
            if (currentApiVersion >= Build.VERSION_CODES.KITKAT) {
                window.decorView.systemUiVisibility = flags
                // Code below is to handle presses of Volume up or Volume down.
                // Without this, after pressing volume buttons, the navigation bar will
                // show up and won't hide
                val decorView = window.decorView
                decorView.setOnSystemUiVisibilityChangeListener { visibility: Int ->
                    if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
                        decorView.systemUiVisibility = flags
                    }
                }
            }
        }
Stevie
  • 401
  • 4
  • 13
1

According to Chris Banes @ Android Developers official youtube channel use following code

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var view: View

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        view = binding.root
        setContentView(view)
    }

    override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        if (hasFocus) hideSystemUI()
    }

    private fun hideSystemUI() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            // Tell the window that we want to handle/fit any system windows
            WindowCompat.setDecorFitsSystemWindows(window, false)

            val controller = view.windowInsetsController

            // Hide the keyboard (IME)
            controller?.hide(WindowInsets.Type.ime())

            // Sticky Immersive is now ...
            controller?.systemBarsBehavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE

            // When we want to hide the system bars
            controller?.hide(WindowInsets.Type.systemBars())

            /*val flag = WindowInsets.Type.statusBars()
            WindowInsets.Type.navigationBars()
            WindowInsets.Type.captionBar()
            window?.insetsController?.hide(flag)*/
        } else {
            //noinspection
            @Suppress("DEPRECATION")
            // For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE.
            window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                // Set the content to appear under the system bars so that the
                // content doesn't resize when the system bars hide and show.
                or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                // Hide the nav bar and status bar
                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_FULLSCREEN)
        }
    }
}

Here is Sample Link User interface sample

Vidyesh Churi
  • 1,899
  • 1
  • 24
  • 33
  • Your answer is outdated. There's no need to use the deprecated flags in the `else` block. See accepted answer for the correct solution – James Sep 29 '21 at 20:19
0

//you can do it by the following simple method

public void FullScreencall() {
  if (Build.VERSION.SDK_INT < 19) {
    getWindow().getDecorView().setSystemUiVisibility(8);
  } else {
    getWindow().getDecorView().setSystemUiVisibility(InputDeviceCompat.SOURCE_TOUCHSCREEN);       
  }
}
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
Mazhar Iqbal
  • 813
  • 7
  • 7