2020import androidx .annotation .NonNull ;
2121import androidx .annotation .Nullable ;
2222import androidx .annotation .VisibleForTesting ;
23- import androidx .core .view .WindowCompat ;
24- import androidx .core .view .WindowInsetsCompat ;
2523import androidx .core .view .WindowInsetsControllerCompat ;
2624import io .flutter .Log ;
2725import io .flutter .embedding .engine .systemchannels .PlatformChannel ;
3028
3129/** Android implementation of the platform plugin. */
3230public 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 PlatformChannel .SystemUiMode currentSystemUiMode ;
39- private List <PlatformChannel .SystemUiOverlay > currentOverlays ;
38+ private int mEnabledOverlays ;
4039 private static final String TAG = "PlatformPlugin" ;
4140
4241 /**
@@ -143,6 +142,8 @@ public PlatformPlugin(
143142 this .platformChannel = platformChannel ;
144143 this .platformChannel .setPlatformMessageHandler (mPlatformMessageHandler );
145144 this .platformPluginDelegate = delegate ;
145+
146+ mEnabledOverlays = DEFAULT_SYSTEM_UI ;
146147 }
147148
148149 /**
@@ -240,75 +241,17 @@ public void onSystemUiVisibilityChange(int visibility) {
240241 }
241242
242243 private void setSystemChromeEnabledSystemUIMode (PlatformChannel .SystemUiMode systemUiMode ) {
243- if (Build .VERSION .SDK_INT <= 19 ) {
244- // As of API 30, the Android APIs for overlays/insets provides backwards compatibility back
245- // through API 19. Since Flutter currently supports API 19, the legacy code is used for
246- // that case.
247- setSystemChromeEnabledSystemUIModeLegacy (systemUiMode );
248- } else {
249- Window window = activity .getWindow ();
250- View view = window .getDecorView ();
251- WindowInsetsControllerCompat windowInsetsControllerCompat =
252- new WindowInsetsControllerCompat (window , view );
253-
254- if (systemUiMode == PlatformChannel .SystemUiMode .LEAN_BACK ) {
255- // LEAN BACK
256- // Available starting at SDK 16. Implemented for API 20+ here.
257- // Should not show overlays, tap to reveal overlays, needs onChange callback
258- // When the overlays come in on tap, the app does not receive the gesture and does not know
259- // the system overlay has changed. The overlays cannot be dismissed, so adding the callback
260- // support will allow users to restore the system ui and dismiss the overlays.
261- // Not compatible with top/bottom overlays enabled.
262- windowInsetsControllerCompat .setSystemBarsBehavior (
263- WindowInsetsControllerCompat .BEHAVIOR_SHOW_BARS_BY_TOUCH );
264- windowInsetsControllerCompat .hide (WindowInsetsCompat .Type .systemBars ());
265- WindowCompat .setDecorFitsSystemWindows (window , false );
266- } else if (systemUiMode == PlatformChannel .SystemUiMode .IMMERSIVE ) {
267- // IMMERSIVE
268- // Available starting at SDK 19. Implemented for API 20+ here.
269- // Should not show overlays, swipe from edges to reveal overlays, needs onChange callback
270- // When the overlays come in on swipe, the app does not receive the gesture and does not
271- // know the system overlay has changed. The overlays cannot be dismissed, so adding callback
272- // support will allow users to restore the system ui and dismiss the overlays.
273- // Not compatible with top/bottom overlays enabled.
274- windowInsetsControllerCompat .setSystemBarsBehavior (
275- WindowInsetsControllerCompat .BEHAVIOR_SHOW_BARS_BY_SWIPE );
276- windowInsetsControllerCompat .hide (WindowInsetsCompat .Type .systemBars ());
277- WindowCompat .setDecorFitsSystemWindows (window , false );
278- } else if (systemUiMode == PlatformChannel .SystemUiMode .IMMERSIVE_STICKY ) {
279- // STICKY IMMERSIVE
280- // Available starting at SDK 19. Implemented for API 20+ here.
281- // Should not show overlays, swipe from edges to reveal overlays. The app will also receive
282- // the swipe gesture. The overlays cannot be dismissed, so adding callback support will
283- // allow users to restore the system ui and dismiss the overlays.
284- // Not compatible with top/bottom overlays enabled.
285- windowInsetsControllerCompat .setSystemBarsBehavior (
286- WindowInsetsControllerCompat .BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE );
287- windowInsetsControllerCompat .hide (WindowInsetsCompat .Type .systemBars ());
288- WindowCompat .setDecorFitsSystemWindows (window , false );
289- } else if (systemUiMode == PlatformChannel .SystemUiMode .EDGE_TO_EDGE
290- && Build .VERSION .SDK_INT >= 29 ) {
291- // EDGE TO EDGE
292- // Available starting at SDK 29. See issue for context:
293- // https://github.com/flutter/flutter/issues/89774.
294- // Will apply a translucent body scrim behind 2/3 button navigation bars
295- // to ensure contrast with buttons on the nav and status bars, unless the contrast is not
296- // enforced in the overlay styling.
297- WindowCompat .setDecorFitsSystemWindows (window , false );
298- } else {
299- // When none of the conditions are matched, return without updating the system UI overlays.
300- return ;
301- }
302- }
303- currentSystemUiMode = systemUiMode ;
304- }
305-
306- private void setSystemChromeEnabledSystemUIModeLegacy (PlatformChannel .SystemUiMode systemUiMode ) {
307244 int enabledOverlays ;
308245
309- if (systemUiMode == PlatformChannel .SystemUiMode .LEAN_BACK ) {
246+ if (systemUiMode == PlatformChannel .SystemUiMode .LEAN_BACK
247+ && Build .VERSION .SDK_INT >= Build .VERSION_CODES .JELLY_BEAN ) {
310248 // LEAN BACK
311- // Available starting at SDK 16. Implemented for APIs 16-19 here.
249+ // Available starting at SDK 16
250+ // Should not show overlays, tap to reveal overlays, needs onChange callback
251+ // When the overlays come in on tap, the app does not receive the gesture and does not know
252+ // the system overlay has changed. The overlays cannot be dismissed, so adding the callback
253+ // support will allow users to restore the system ui and dismiss the overlays.
254+ // Not compatible with top/bottom overlays enabled.
312255 enabledOverlays =
313256 View .SYSTEM_UI_FLAG_LAYOUT_STABLE
314257 | View .SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
@@ -318,8 +261,12 @@ private void setSystemChromeEnabledSystemUIModeLegacy(PlatformChannel.SystemUiMo
318261 } else if (systemUiMode == PlatformChannel .SystemUiMode .IMMERSIVE
319262 && Build .VERSION .SDK_INT >= Build .VERSION_CODES .KITKAT ) {
320263 // IMMERSIVE
321- // Available starting at SDK 19. Implemented for API 19 here. Earlier versions will not be
322- // affected by this.
264+ // Available starting at 19
265+ // Should not show overlays, swipe from edges to reveal overlays, needs onChange callback
266+ // When the overlays come in on swipe, the app does not receive the gesture and does not know
267+ // the system overlay has changed. The overlays cannot be dismissed, so adding callback
268+ // support will allow users to restore the system ui and dismiss the overlays.
269+ // Not compatible with top/bottom overlays enabled.
323270 enabledOverlays =
324271 View .SYSTEM_UI_FLAG_IMMERSIVE
325272 | View .SYSTEM_UI_FLAG_LAYOUT_STABLE
@@ -330,78 +277,56 @@ private void setSystemChromeEnabledSystemUIModeLegacy(PlatformChannel.SystemUiMo
330277 } else if (systemUiMode == PlatformChannel .SystemUiMode .IMMERSIVE_STICKY
331278 && Build .VERSION .SDK_INT >= Build .VERSION_CODES .KITKAT ) {
332279 // STICKY IMMERSIVE
333- // Available starting at SDK 19. Implemented for API 19 here. Earlier versions will not be
334- // affected by this.
280+ // Available starting at 19
281+ // Should not show overlays, swipe from edges to reveal overlays. The app will also receive
282+ // the swipe gesture. The overlays cannot be dismissed, so adding callback support will
283+ // allow users to restore the system ui and dismiss the overlays.
284+ // Not compatible with top/bottom overlays enabled.
335285 enabledOverlays =
336286 View .SYSTEM_UI_FLAG_IMMERSIVE_STICKY
337287 | View .SYSTEM_UI_FLAG_LAYOUT_STABLE
338288 | View .SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
339289 | View .SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
340290 | View .SYSTEM_UI_FLAG_HIDE_NAVIGATION
341291 | View .SYSTEM_UI_FLAG_FULLSCREEN ;
292+ } else if (systemUiMode == PlatformChannel .SystemUiMode .EDGE_TO_EDGE
293+ && Build .VERSION .SDK_INT >= 29 ) {
294+ // EDGE TO EDGE
295+ // Available starting at 29
296+ // SDK 29 and up will apply a translucent body scrim behind 2/3 button navigation bars
297+ // to ensure contrast with buttons on the nav and status bars, unless the contrast is not
298+ // enforced in the overlay styling.
299+ enabledOverlays =
300+ View .SYSTEM_UI_FLAG_LAYOUT_STABLE
301+ | View .SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
302+ | View .SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ;
342303 } else {
304+ // When none of the conditions are matched, return without updating the system UI overlays.
343305 return ;
344306 }
345- activity .getWindow ().getDecorView ().setSystemUiVisibility (enabledOverlays );
346- }
347-
348- private void setSystemChromeEnabledSystemUIOverlays (
349- List <PlatformChannel .SystemUiOverlay > overlaysToShow ) {
350- if (Build .VERSION .SDK_INT <= 19 ) {
351- // As of API 30, the Android APIs for overlays/insets provides backwards compatibility back
352- // through API 19. Since Flutter currently supports API 19, the legacy code is used
353- // for that case.
354- setSystemChromeEnabledSystemUIOverlaysLegacy (overlaysToShow );
355- } else {
356- Window window = activity .getWindow ();
357- View view = window .getDecorView ();
358- WindowInsetsControllerCompat windowInsetsControllerCompat =
359- new WindowInsetsControllerCompat (window , view );
360-
361- // Start by assuming we want to hide all system overlays (like an immersive
362- // game).
363- windowInsetsControllerCompat .hide (WindowInsetsCompat .Type .systemBars ());
364- WindowCompat .setDecorFitsSystemWindows (window , false );
365-
366- // We apply sticky immersive mode if desired. Available starting at SDK 20.
367- if (overlaysToShow .size () == 0 ) {
368- currentSystemUiMode = PlatformChannel .SystemUiMode .IMMERSIVE_STICKY ;
369- windowInsetsControllerCompat .setSystemBarsBehavior (
370- WindowInsetsControllerCompat .BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE );
371- }
372307
373- // Re-add any desired system overlays.
374- for (int i = 0 ; i < overlaysToShow .size (); ++i ) {
375- PlatformChannel .SystemUiOverlay overlayToShow = overlaysToShow .get (i );
376- switch (overlayToShow ) {
377- case TOP_OVERLAYS :
378- windowInsetsControllerCompat .show (WindowInsetsCompat .Type .statusBars ());
379- break ;
380- case BOTTOM_OVERLAYS :
381- windowInsetsControllerCompat .show (WindowInsetsCompat .Type .navigationBars ());
382- break ;
383- }
384- }
385- }
386- currentOverlays = overlaysToShow ;
308+ mEnabledOverlays = enabledOverlays ;
309+ updateSystemUiOverlays ();
387310 }
388311
389- private void setSystemChromeEnabledSystemUIOverlaysLegacy (
312+ private void setSystemChromeEnabledSystemUIOverlays (
390313 List <PlatformChannel .SystemUiOverlay > overlaysToShow ) {
314+ // Start by assuming we want to hide all system overlays (like an immersive
315+ // game).
391316 int enabledOverlays =
392- View .SYSTEM_UI_FLAG_LAYOUT_STABLE
393- | View .SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
317+ DEFAULT_SYSTEM_UI
394318 | View .SYSTEM_UI_FLAG_FULLSCREEN
395319 | View .SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
396320 | View .SYSTEM_UI_FLAG_HIDE_NAVIGATION ;
397321
398322 // The SYSTEM_UI_FLAG_IMMERSIVE_STICKY flag was introduced in API 19, so we
399- // apply it if desired.
323+ // apply it
324+ // if desired, and if the current Android version is 19 or greater.
400325 if (overlaysToShow .size () == 0 && Build .VERSION .SDK_INT >= Build .VERSION_CODES .KITKAT ) {
401- currentSystemUiMode = PlatformChannel .SystemUiMode .IMMERSIVE_STICKY ;
402326 enabledOverlays |= View .SYSTEM_UI_FLAG_IMMERSIVE_STICKY ;
403327 }
404328
329+ // Re-add any desired system overlays.
405330 for (int i = 0 ; i < overlaysToShow .size (); ++i ) {
406331 PlatformChannel .SystemUiOverlay overlayToShow = overlaysToShow .get (i );
407332 switch (overlayToShow ) {
@@ -414,7 +339,9 @@ private void setSystemChromeEnabledSystemUIOverlaysLegacy(
414339 break ;
415340 }
416341 }
417- activity .getWindow ().getDecorView ().setSystemUiVisibility (enabledOverlays );
342+
343+ mEnabledOverlays = enabledOverlays ;
344+ updateSystemUiOverlays ();
418345 }
419346
420347 /**
@@ -426,26 +353,7 @@ private void setSystemChromeEnabledSystemUIOverlaysLegacy(
426353 * PlatformPlugin}.
427354 */
428355 public void updateSystemUiOverlays () {
429- if (currentOverlays != null ) {
430- setSystemChromeEnabledSystemUIOverlays (currentOverlays );
431-
432- if (currentOverlays .size () > 0 ) {
433- if (currentSystemUiMode != null ) {
434- setSystemChromeEnabledSystemUIMode (currentSystemUiMode );
435- } else {
436- Window window = activity .getWindow ();
437- WindowCompat .setDecorFitsSystemWindows (window , false );
438- if (Build .VERSION .SDK_INT < 30 ) {
439- // This ensures that the navigation bar is not hidden for APIs < 30,
440- // as dictated by the implementation of WindowCompat.
441- View view = window .getDecorView ();
442- view .setSystemUiVisibility (
443- view .getSystemUiVisibility () & ~View .SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION );
444- }
445- }
446- }
447- }
448-
356+ activity .getWindow ().getDecorView ().setSystemUiVisibility (mEnabledOverlays );
449357 if (currentTheme != null ) {
450358 setSystemChromeSystemUIOverlayStyle (currentTheme );
451359 }
0 commit comments