2020import androidx .annotation .NonNull ;
2121import androidx .annotation .Nullable ;
2222import androidx .annotation .VisibleForTesting ;
23+ import androidx .core .view .WindowCompat ;
24+ import androidx .core .view .WindowInsetsCompat ;
2325import androidx .core .view .WindowInsetsControllerCompat ;
2426import io .flutter .Log ;
2527import io .flutter .embedding .engine .systemchannels .PlatformChannel ;
2830
2931/** Android implementation of the platform plugin. */
3032public class PlatformPlugin {
31- public static final int DEFAULT_SYSTEM_UI =
32- View .SYSTEM_UI_FLAG_LAYOUT_STABLE | View .SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ;
3333
3434 private final Activity activity ;
3535 private final PlatformChannel platformChannel ;
3636 private final PlatformPluginDelegate platformPluginDelegate ;
3737 private PlatformChannel .SystemChromeStyle currentTheme ;
38- private int mEnabledOverlays ;
38+ private PlatformChannel .SystemUiMode currentSystemUiMode ;
39+ private List <PlatformChannel .SystemUiOverlay > currentOverlays ;
3940 private static final String TAG = "PlatformPlugin" ;
4041
4142 /**
@@ -141,7 +142,7 @@ public PlatformPlugin(
141142 this .platformChannel .setPlatformMessageHandler (mPlatformMessageHandler );
142143 this .platformPluginDelegate = delegate ;
143144
144- mEnabledOverlays = DEFAULT_SYSTEM_UI ;
145+ currentSystemUiMode = PlatformChannel . SystemUiMode . EDGE_TO_EDGE ;
145146 }
146147
147148 /**
@@ -240,105 +241,98 @@ public void onSystemUiVisibilityChange(int visibility) {
240241 private void setSystemChromeEnabledSystemUIMode (PlatformChannel .SystemUiMode systemUiMode ) {
241242 int enabledOverlays ;
242243
243- if (systemUiMode == PlatformChannel .SystemUiMode .LEAN_BACK
244- && Build .VERSION .SDK_INT >= Build .VERSION_CODES .JELLY_BEAN ) {
244+ Window window = activity .getWindow ();
245+ View view = window .getDecorView ();
246+ WindowInsetsControllerCompat windowInsetsControllerCompat =
247+ new WindowInsetsControllerCompat (window , view );
248+
249+ if (systemUiMode == PlatformChannel .SystemUiMode .LEAN_BACK ) {
245250 // LEAN BACK
246- // Available starting at SDK 16
251+ // Available starting at SDK 20, due to the backwards compatibility provided by the
252+ // WindowInsetsControllerCompat class for setting the behavior of system bars.
247253 // Should not show overlays, tap to reveal overlays, needs onChange callback
248254 // When the overlays come in on tap, the app does not receive the gesture and does not know
249255 // the system overlay has changed. The overlays cannot be dismissed, so adding the callback
250256 // support will allow users to restore the system ui and dismiss the overlays.
251257 // Not compatible with top/bottom overlays enabled.
252- enabledOverlays =
253- View .SYSTEM_UI_FLAG_LAYOUT_STABLE
254- | View .SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
255- | View .SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
256- | View .SYSTEM_UI_FLAG_HIDE_NAVIGATION
257- | View .SYSTEM_UI_FLAG_FULLSCREEN ;
258- } else if (systemUiMode == PlatformChannel .SystemUiMode .IMMERSIVE
259- && Build .VERSION .SDK_INT >= Build .VERSION_CODES .KITKAT ) {
258+ windowInsetsControllerCompat .setSystemBarsBehavior (
259+ WindowInsetsControllerCompat .BEHAVIOR_SHOW_BARS_BY_TOUCH );
260+ windowInsetsControllerCompat .hide (WindowInsetsCompat .Type .systemBars ());
261+ WindowCompat .setDecorFitsSystemWindows (window , false );
262+ } else if (systemUiMode == PlatformChannel .SystemUiMode .IMMERSIVE ) {
260263 // IMMERSIVE
261- // Available starting at 19
264+ // Available starting at SDK 20, due to the backwards compatibility provided by the
265+ // WindowInsetsControllerCompat class for setting the behavior of system bars.
262266 // Should not show overlays, swipe from edges to reveal overlays, needs onChange callback
263267 // When the overlays come in on swipe, the app does not receive the gesture and does not know
264268 // the system overlay has changed. The overlays cannot be dismissed, so adding callback
265269 // support will allow users to restore the system ui and dismiss the overlays.
266270 // Not compatible with top/bottom overlays enabled.
267- enabledOverlays =
268- View .SYSTEM_UI_FLAG_IMMERSIVE
269- | View .SYSTEM_UI_FLAG_LAYOUT_STABLE
270- | View .SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
271- | View .SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
272- | View .SYSTEM_UI_FLAG_HIDE_NAVIGATION
273- | View .SYSTEM_UI_FLAG_FULLSCREEN ;
274- } else if (systemUiMode == PlatformChannel .SystemUiMode .IMMERSIVE_STICKY
275- && Build .VERSION .SDK_INT >= Build .VERSION_CODES .KITKAT ) {
271+ windowInsetsControllerCompat .setSystemBarsBehavior (
272+ WindowInsetsControllerCompat .BEHAVIOR_SHOW_BARS_BY_SWIPE );
273+ windowInsetsControllerCompat .hide (WindowInsetsCompat .Type .systemBars ());
274+ WindowCompat .setDecorFitsSystemWindows (window , false );
275+ } else if (systemUiMode == PlatformChannel .SystemUiMode .IMMERSIVE_STICKY ) {
276276 // STICKY IMMERSIVE
277- // Available starting at 19
277+ // Available starting at SDK 20, due to the backwards compatibility provided by the
278+ // WindowInsetsControllerCompat class for setting the behavior of system bars.
278279 // Should not show overlays, swipe from edges to reveal overlays. The app will also receive
279280 // the swipe gesture. The overlays cannot be dismissed, so adding callback support will
280281 // allow users to restore the system ui and dismiss the overlays.
281282 // Not compatible with top/bottom overlays enabled.
282- enabledOverlays =
283- View .SYSTEM_UI_FLAG_IMMERSIVE_STICKY
284- | View .SYSTEM_UI_FLAG_LAYOUT_STABLE
285- | View .SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
286- | View .SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
287- | View .SYSTEM_UI_FLAG_HIDE_NAVIGATION
288- | View .SYSTEM_UI_FLAG_FULLSCREEN ;
283+ windowInsetsControllerCompat .setSystemBarsBehavior (
284+ WindowInsetsControllerCompat .BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE );
285+ windowInsetsControllerCompat .hide (WindowInsetsCompat .Type .systemBars ());
286+ WindowCompat .setDecorFitsSystemWindows (window , false );
289287 } else if (systemUiMode == PlatformChannel .SystemUiMode .EDGE_TO_EDGE
290288 && Build .VERSION .SDK_INT >= 29 ) {
291289 // EDGE TO EDGE
292- // Available starting at 29
293- // SDK 29 and up will apply a translucent body scrim behind 2/3 button navigation bars
290+ // Available starting at SDK 29. See issue for context:
291+ // https://github.com/flutter/flutter/issues/89774.
292+ // Will apply a translucent body scrim behind 2/3 button navigation bars
294293 // to ensure contrast with buttons on the nav and status bars, unless the contrast is not
295294 // enforced in the overlay styling.
296- enabledOverlays =
297- View .SYSTEM_UI_FLAG_LAYOUT_STABLE
298- | View .SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
299- | View .SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ;
295+ WindowCompat .setDecorFitsSystemWindows (window , false );
300296 } else {
301297 // When none of the conditions are matched, return without updating the system UI overlays.
302298 return ;
303299 }
304-
305- mEnabledOverlays = enabledOverlays ;
306- updateSystemUiOverlays ();
300+ currentSystemUiMode = systemUiMode ;
307301 }
308302
309303 private void setSystemChromeEnabledSystemUIOverlays (
310304 List <PlatformChannel .SystemUiOverlay > overlaysToShow ) {
305+ Window window = activity .getWindow ();
306+ View view = window .getDecorView ();
307+ WindowInsetsControllerCompat windowInsetsControllerCompat =
308+ new WindowInsetsControllerCompat (window , view );
309+
311310 // Start by assuming we want to hide all system overlays (like an immersive
312311 // game).
313- int enabledOverlays =
314- DEFAULT_SYSTEM_UI
315- | View .SYSTEM_UI_FLAG_FULLSCREEN
316- | View .SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
317- | View .SYSTEM_UI_FLAG_HIDE_NAVIGATION ;
318-
319- // The SYSTEM_UI_FLAG_IMMERSIVE_STICKY flag was introduced in API 19, so we
320- // apply it
321- // if desired, and if the current Android version is 19 or greater.
322- if (overlaysToShow .size () == 0 && Build .VERSION .SDK_INT >= Build .VERSION_CODES .KITKAT ) {
323- enabledOverlays |= View .SYSTEM_UI_FLAG_IMMERSIVE_STICKY ;
312+ windowInsetsControllerCompat .hide (WindowInsetsCompat .Type .systemBars ());
313+ WindowCompat .setDecorFitsSystemWindows (window , false );
314+
315+ // We apply sticky immersive mode if desired. Available starting at SDK 20.
316+ if (overlaysToShow .size () == 0 ) {
317+ currentSystemUiMode = PlatformChannel .SystemUiMode .IMMERSIVE_STICKY ;
318+
319+ windowInsetsControllerCompat .setSystemBarsBehavior (
320+ WindowInsetsControllerCompat .BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE );
324321 }
325322
326323 // Re-add any desired system overlays.
327324 for (int i = 0 ; i < overlaysToShow .size (); ++i ) {
328325 PlatformChannel .SystemUiOverlay overlayToShow = overlaysToShow .get (i );
329326 switch (overlayToShow ) {
330327 case TOP_OVERLAYS :
331- enabledOverlays &= ~ View . SYSTEM_UI_FLAG_FULLSCREEN ;
328+ windowInsetsControllerCompat . show ( WindowInsetsCompat . Type . statusBars ()) ;
332329 break ;
333330 case BOTTOM_OVERLAYS :
334- enabledOverlays &= ~View .SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION ;
335- enabledOverlays &= ~View .SYSTEM_UI_FLAG_HIDE_NAVIGATION ;
331+ windowInsetsControllerCompat .show (WindowInsetsCompat .Type .navigationBars ());
336332 break ;
337333 }
338334 }
339-
340- mEnabledOverlays = enabledOverlays ;
341- updateSystemUiOverlays ();
335+ currentOverlays = overlaysToShow ;
342336 }
343337
344338 /**
@@ -350,8 +344,11 @@ private void setSystemChromeEnabledSystemUIOverlays(
350344 * PlatformPlugin}.
351345 */
352346 public void updateSystemUiOverlays () {
353- activity .getWindow ().getDecorView ().setSystemUiVisibility (mEnabledOverlays );
354- if (currentTheme != null ) {
347+ setSystemChromeEnabledSystemUIMode (currentSystemUiMode );
348+
349+ if (currentOverlays != null ) {
350+ setSystemChromeEnabledSystemUIOverlays (currentOverlays );
351+ } else if (currentTheme != null ) {
355352 setSystemChromeSystemUIOverlayStyle (currentTheme );
356353 }
357354 }
0 commit comments