From b9e2d723fecceadb8fb5265914215cd2ccff13ed Mon Sep 17 00:00:00 2001 From: camsim99 Date: Wed, 26 Jan 2022 12:10:05 -0800 Subject: [PATCH 01/14] Add version check --- .../android/io/flutter/app/FlutterActivityDelegate.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java index 0fe692c49c6c7..2c6be89c70908 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java @@ -141,7 +141,9 @@ public void onCreate(Bundle savedInstanceState) { Window window = activity.getWindow(); window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); - window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI); + if (Build.VERSION.SDK_INT <= 19) { + window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI); + } } String[] args = getArgsFromIntent(activity.getIntent()); From 43c4ad02449f74882d2350c39e402c8024e9a3bd Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 24 Jan 2022 16:27:04 -0800 Subject: [PATCH 02/14] Remove setSystemUiVisibility usages --- .../flutter/app/FlutterActivityDelegate.java | 2 +- .../embedding/android/FlutterActivity.java | 4 +- .../android/FlutterFragmentActivity.java | 4 +- .../plugin/platform/PlatformPlugin.java | 181 +++++++++++++----- .../plugin/platform/PlatformPluginTest.java | 168 ++++++++++++++-- 5 files changed, 290 insertions(+), 69 deletions(-) diff --git a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java index 2c6be89c70908..2e435f872367c 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java @@ -142,7 +142,7 @@ public void onCreate(Bundle savedInstanceState) { window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); if (Build.VERSION.SDK_INT <= 19) { - window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI); + window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI_LEGACY); } } diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 2b915cc04ce3b..0368f0103ab05 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -590,7 +590,9 @@ private void configureStatusBarForFullscreenFlutterExperience() { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); - window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI); + if (Build.VERSION.SDK_INT <= 19) { + window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI_LEGACY); + } } } diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index d7c8515b4869d..2fb1cb58691e4 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -495,7 +495,9 @@ private void configureStatusBarForFullscreenFlutterExperience() { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); - window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI); + if (Build.VERSION.SDK_INT <= 19) { + window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI_LEGACY); + } } } diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index 59733b29bd0b0..719a94e482749 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -20,6 +20,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.core.view.WindowInsetsControllerCompat; import io.flutter.Log; import io.flutter.embedding.engine.systemchannels.PlatformChannel; @@ -28,14 +30,16 @@ /** Android implementation of the platform plugin. */ public class PlatformPlugin { - public static final int DEFAULT_SYSTEM_UI = + // These flags are used on API 16-19 to define the default layout. + public static final int DEFAULT_SYSTEM_UI_LEGACY = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; private final Activity activity; private final PlatformChannel platformChannel; private final PlatformPluginDelegate platformPluginDelegate; private PlatformChannel.SystemChromeStyle currentTheme; - private int mEnabledOverlays; + private PlatformChannel.SystemUiMode currentSystemUiMode; + private List currentOverlays; private static final String TAG = "PlatformPlugin"; /** @@ -140,8 +144,6 @@ public PlatformPlugin( this.platformChannel = platformChannel; this.platformChannel.setPlatformMessageHandler(mPlatformMessageHandler); this.platformPluginDelegate = delegate; - - mEnabledOverlays = DEFAULT_SYSTEM_UI; } /** @@ -237,18 +239,11 @@ public void onSystemUiVisibilityChange(int visibility) { }); } - private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode systemUiMode) { + private void setSystemChromeEnabledSystemUIModeLegacy(PlatformChannel.SystemUiMode systemUiMode) { int enabledOverlays; - if (systemUiMode == PlatformChannel.SystemUiMode.LEAN_BACK - && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + if (systemUiMode == PlatformChannel.SystemUiMode.LEAN_BACK) { // LEAN BACK - // Available starting at SDK 16 - // Should not show overlays, tap to reveal overlays, needs onChange callback - // When the overlays come in on tap, the app does not receive the gesture and does not know - // the system overlay has changed. The overlays cannot be dismissed, so adding the callback - // support will allow users to restore the system ui and dismiss the overlays. - // Not compatible with top/bottom overlays enabled. enabledOverlays = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION @@ -258,12 +253,6 @@ private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode sys } else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // IMMERSIVE - // Available starting at 19 - // Should not show overlays, swipe from edges to reveal overlays, needs onChange callback - // When the overlays come in on swipe, the app does not receive the gesture and does not know - // the system overlay has changed. The overlays cannot be dismissed, so adding callback - // support will allow users to restore the system ui and dismiss the overlays. - // Not compatible with top/bottom overlays enabled. enabledOverlays = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE @@ -274,11 +263,6 @@ private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode sys } else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE_STICKY && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // STICKY IMMERSIVE - // Available starting at 19 - // Should not show overlays, swipe from edges to reveal overlays. The app will also receive - // the swipe gesture. The overlays cannot be dismissed, so adding callback support will - // allow users to restore the system ui and dismiss the overlays. - // Not compatible with top/bottom overlays enabled. enabledOverlays = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_STABLE @@ -286,44 +270,96 @@ private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode sys | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN; - } else if (systemUiMode == PlatformChannel.SystemUiMode.EDGE_TO_EDGE - && Build.VERSION.SDK_INT >= 29) { - // EDGE TO EDGE - // Available starting at 29 - // SDK 29 and up will apply a translucent body scrim behind 2/3 button navigation bars - // to ensure contrast with buttons on the nav and status bars, unless the contrast is not - // enforced in the overlay styling. - enabledOverlays = - View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; } else { // When none of the conditions are matched, return without updating the system UI overlays. return; } - mEnabledOverlays = enabledOverlays; - updateSystemUiOverlays(); + activity.getWindow().getDecorView().setSystemUiVisibility(enabledOverlays); } - private void setSystemChromeEnabledSystemUIOverlays( + private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode systemUiMode) { + // As of API 30, controlling insets and overlays changed significantly, but provided backwards + // compatibility back through API 20. Flutter currently supports API 19, so the legacy code is + // left for that case. + if (Build.VERSION.SDK_INT <= 19) { + setSystemChromeEnabledSystemUIModeLegacy(systemUiMode); + } else { + Window window = activity.getWindow(); + View view = window.getDecorView(); + WindowInsetsControllerCompat windowInsetsControllerCompat = + new WindowInsetsControllerCompat(window, view); + + if (systemUiMode == PlatformChannel.SystemUiMode.LEAN_BACK) { + // LEAN BACK + // Available starting at SDK 20, due to the backwards compatibility provided by the + // WindowInsetsControllerCompat class for setting the behavior of system bars. + // Should not show overlays, tap to reveal overlays, needs onChange callback + // When the overlays come in on tap, the app does not receive the gesture and does not know + // the system overlay has changed. The overlays cannot be dismissed, so adding the callback + // support will allow users to restore the system ui and dismiss the overlays. + // Not compatible with top/bottom overlays enabled. + windowInsetsControllerCompat.setSystemBarsBehavior( + WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_TOUCH); + windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.systemBars()); + WindowCompat.setDecorFitsSystemWindows(window, false); + } else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE) { + // IMMERSIVE + // Available starting at SDK 20, due to the backwards compatibility provided by the + // WindowInsetsControllerCompat class for setting the behavior of system bars. + // Should not show overlays, swipe from edges to reveal overlays, needs onChange callback + // When the overlays come in on swipe, the app does not receive the gesture and does not + // know + // the system overlay has changed. The overlays cannot be dismissed, so adding callback + // support will allow users to restore the system ui and dismiss the overlays. + // Not compatible with top/bottom overlays enabled. + windowInsetsControllerCompat.setSystemBarsBehavior( + WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_SWIPE); + windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.systemBars()); + WindowCompat.setDecorFitsSystemWindows(window, false); + } else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE_STICKY) { + // STICKY IMMERSIVE + // Available starting at SDK 20, due to the backwards compatibility provided by the + // WindowInsetsControllerCompat class for setting the behavior of system bars. + // Should not show overlays, swipe from edges to reveal overlays. The app will also receive + // the swipe gesture. The overlays cannot be dismissed, so adding callback support will + // allow users to restore the system ui and dismiss the overlays. + // Not compatible with top/bottom overlays enabled. + windowInsetsControllerCompat.setSystemBarsBehavior( + WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.systemBars()); + WindowCompat.setDecorFitsSystemWindows(window, false); + } else if (systemUiMode == PlatformChannel.SystemUiMode.EDGE_TO_EDGE + && Build.VERSION.SDK_INT >= 29) { + // EDGE TO EDGE + // Available starting at SDK 29. See issue for context: + // https://github.com/flutter/flutter/issues/89774. + // Will apply a translucent body scrim behind 2/3 button navigation bars + // to ensure contrast with buttons on the nav and status bars, unless the contrast is not + // enforced in the overlay styling. + WindowCompat.setDecorFitsSystemWindows(window, false); + } else { + // When none of the conditions are matched, return without updating the system UI overlays. + return; + } + } + currentSystemUiMode = systemUiMode; + } + + private void setSystemChromeEnabledSystemUIOverlaysLegacy( List overlaysToShow) { - // Start by assuming we want to hide all system overlays (like an immersive - // game). int enabledOverlays = - DEFAULT_SYSTEM_UI + DEFAULT_SYSTEM_UI_LEGACY | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; // The SYSTEM_UI_FLAG_IMMERSIVE_STICKY flag was introduced in API 19, so we - // apply it - // if desired, and if the current Android version is 19 or greater. + // apply it if desired. if (overlaysToShow.size() == 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { enabledOverlays |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; } - // Re-add any desired system overlays. for (int i = 0; i < overlaysToShow.size(); ++i) { PlatformChannel.SystemUiOverlay overlayToShow = overlaysToShow.get(i); switch (overlayToShow) { @@ -337,8 +373,48 @@ private void setSystemChromeEnabledSystemUIOverlays( } } - mEnabledOverlays = enabledOverlays; - updateSystemUiOverlays(); + activity.getWindow().getDecorView().setSystemUiVisibility(enabledOverlays); + } + + private void setSystemChromeEnabledSystemUIOverlays( + List overlaysToShow) { + if (Build.VERSION.SDK_INT <= 19) { + // This method calls offer support for the same behaviors below for API 19 that is unsupported + // by Android new overlays/insets API that offers backward compatibility only up to API 20. + setSystemChromeEnabledSystemUIOverlaysLegacy(overlaysToShow); + } else { + Window window = activity.getWindow(); + View view = window.getDecorView(); + WindowInsetsControllerCompat windowInsetsControllerCompat = + new WindowInsetsControllerCompat(window, view); + + // Start by assuming we want to hide all system overlays (like an immersive + // game). + windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.systemBars()); + WindowCompat.setDecorFitsSystemWindows(window, false); + + // We apply sticky immersive mode if desired. Available starting at SDK 20. + if (overlaysToShow.size() == 0) { + currentSystemUiMode = PlatformChannel.SystemUiMode.IMMERSIVE_STICKY; + + windowInsetsControllerCompat.setSystemBarsBehavior( + WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + } + + // Re-add any desired system overlays. + for (int i = 0; i < overlaysToShow.size(); ++i) { + PlatformChannel.SystemUiOverlay overlayToShow = overlaysToShow.get(i); + switch (overlayToShow) { + case TOP_OVERLAYS: + windowInsetsControllerCompat.show(WindowInsetsCompat.Type.statusBars()); + break; + case BOTTOM_OVERLAYS: + windowInsetsControllerCompat.show(WindowInsetsCompat.Type.navigationBars()); + break; + } + } + } + currentOverlays = overlaysToShow; } /** @@ -350,9 +426,16 @@ private void setSystemChromeEnabledSystemUIOverlays( * PlatformPlugin}. */ public void updateSystemUiOverlays() { - activity.getWindow().getDecorView().setSystemUiVisibility(mEnabledOverlays); - if (currentTheme != null) { - setSystemChromeSystemUIOverlayStyle(currentTheme); + if (currentOverlays != null) { + setSystemChromeEnabledSystemUIOverlays(currentOverlays); + } else { + if (currentSystemUiMode != null) { + setSystemChromeEnabledSystemUIMode(currentSystemUiMode); + } else if (Build.VERSION.SDK_INT <= 19) { + // On API 19, if there is not a system ui mode already set, we wish to restore the enabled + // flags to the default system ui flags defined above. + activity.getWindow().getDecorView().setSystemUiVisibility(DEFAULT_SYSTEM_UI_LEGACY); + } } } diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java index 3800ddfbff98e..bb9f71214a05e 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -13,6 +13,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.TargetApi; import android.app.Activity; import android.content.ClipData; import android.content.ClipboardManager; @@ -24,6 +25,8 @@ import android.view.Window; import android.view.WindowInsetsController; import androidx.activity.OnBackPressedCallback; +import androidx.core.view.WindowInsetsCompat; +import androidx.core.view.WindowInsetsControllerCompat; import androidx.fragment.app.FragmentActivity; import io.flutter.embedding.engine.systemchannels.PlatformChannel; import io.flutter.embedding.engine.systemchannels.PlatformChannel.Brightness; @@ -31,6 +34,8 @@ import io.flutter.embedding.engine.systemchannels.PlatformChannel.SystemChromeStyle; import io.flutter.plugin.platform.PlatformPlugin.PlatformPluginDelegate; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.junit.runner.RunWith; @@ -313,7 +318,7 @@ public void setStatusBarIconBrightness() { @Config(sdk = 29) @Test - public void setSystemUiMode() { + public void setSystemUiModeLegacy() { View fakeDecorView = mock(View.class); Window fakeWindow = mock(Window.class); when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); @@ -325,41 +330,42 @@ public void setSystemUiMode() { if (Build.VERSION.SDK_INT >= 28) { platformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.LEAN_BACK); + + verify(fakeDecorView).setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN); + verify(fakeDecorView).setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); verify(fakeDecorView) .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_LAYOUT_FULLSCREEN); platformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.IMMERSIVE); - verify(fakeDecorView) + verify(fakeDecorView).setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE); + verify(fakeDecorView, times(2)).setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN); + verify(fakeDecorView, times(2)).setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + verify(fakeDecorView, times(2)) .setSystemUiVisibility( - View.SYSTEM_UI_FLAG_IMMERSIVE - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE + 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_LAYOUT_FULLSCREEN); platformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.IMMERSIVE_STICKY); - verify(fakeDecorView) + verify(fakeDecorView).setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + verify(fakeDecorView, times(3)).setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN); + verify(fakeDecorView, times(3)).setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + verify(fakeDecorView, times(3)) .setSystemUiVisibility( - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE + 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_LAYOUT_FULLSCREEN); } if (Build.VERSION.SDK_INT >= 29) { platformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.EDGE_TO_EDGE); - verify(fakeDecorView) + verify(fakeDecorView, times(4)) .setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION @@ -367,6 +373,134 @@ public void setSystemUiMode() { } } + @TargetApi(30) + @Test + public void setSystemUiMode() { + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + Activity fakeActivity = mock(Activity.class); + when(fakeActivity.getWindow()).thenReturn(fakeWindow); + PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); + PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); + WindowInsetsController fakeWindowInsetsController = mock(WindowInsetsController.class); + when(fakeWindow.getInsetsController()).thenReturn(fakeWindowInsetsController); + + platformPlugin.mPlatformMessageHandler.showSystemUiMode(PlatformChannel.SystemUiMode.LEAN_BACK); + + verify(fakeWindowInsetsController) + .setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_TOUCH); + verify(fakeWindowInsetsController).hide(WindowInsetsCompat.Type.systemBars()); + verify(fakeWindow).setDecorFitsSystemWindows(false); + + platformPlugin.mPlatformMessageHandler.showSystemUiMode(PlatformChannel.SystemUiMode.IMMERSIVE); + + verify(fakeWindowInsetsController) + .setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_SWIPE); + verify(fakeWindowInsetsController, times(2)).hide(WindowInsetsCompat.Type.systemBars()); + verify(fakeWindow, times(2)).setDecorFitsSystemWindows(false); + + platformPlugin.mPlatformMessageHandler.showSystemUiMode( + PlatformChannel.SystemUiMode.IMMERSIVE_STICKY); + + verify(fakeWindowInsetsController) + .setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + verify(fakeWindowInsetsController, times(3)).hide(WindowInsetsCompat.Type.systemBars()); + verify(fakeWindow, times(3)).setDecorFitsSystemWindows(false); + + platformPlugin.mPlatformMessageHandler.showSystemUiMode( + PlatformChannel.SystemUiMode.EDGE_TO_EDGE); + + verify(fakeWindow, times(4)).setDecorFitsSystemWindows(false); + } + + @Config(sdk = 29) + @Test + public void showSystemOverlaysLegacy() { + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + int fakeSetFlags = + View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + Activity fakeActivity = mock(Activity.class); + when(fakeActivity.getWindow()).thenReturn(fakeWindow); + PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); + PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); + + platformPlugin.mPlatformMessageHandler.showSystemOverlays( + new ArrayList()); + + verify(fakeDecorView).setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN); + verify(fakeDecorView).setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + verify(fakeDecorView) + .setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + + verify(fakeDecorView).setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + verify(fakeDecorView).setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN); + verify(fakeDecorView).setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + verify(fakeDecorView) + .setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + + when(fakeDecorView.getSystemUiVisibility()).thenReturn(fakeSetFlags); + platformPlugin.mPlatformMessageHandler.showSystemOverlays( + new ArrayList( + Arrays.asList( + PlatformChannel.SystemUiOverlay.TOP_OVERLAYS, + PlatformChannel.SystemUiOverlay.BOTTOM_OVERLAYS))); + + verify(fakeDecorView).setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN); + verify(fakeDecorView).setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + verify(fakeDecorView) + .setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + + verify(fakeDecorView).setSystemUiVisibility(fakeSetFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN); + verify(fakeDecorView) + .setSystemUiVisibility(fakeSetFlags &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + } + + @TargetApi(30) + @Test + public void showSystemOverlays() { + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + Activity fakeActivity = mock(Activity.class); + when(fakeActivity.getWindow()).thenReturn(fakeWindow); + PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); + PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); + WindowInsetsController fakeWindowInsetsController = mock(WindowInsetsController.class); + when(fakeWindow.getInsetsController()).thenReturn(fakeWindowInsetsController); + + platformPlugin.mPlatformMessageHandler.showSystemOverlays( + new ArrayList()); + + verify(fakeWindowInsetsController) + .setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + verify(fakeWindowInsetsController).hide(WindowInsetsCompat.Type.systemBars()); + + platformPlugin.mPlatformMessageHandler.showSystemOverlays( + new ArrayList( + Arrays.asList( + PlatformChannel.SystemUiOverlay.TOP_OVERLAYS, + PlatformChannel.SystemUiOverlay.BOTTOM_OVERLAYS))); + + verify(fakeWindowInsetsController).show(WindowInsetsCompat.Type.statusBars()); + verify(fakeWindowInsetsController).show(WindowInsetsCompat.Type.navigationBars()); + } + @Config(sdk = 28) @Test public void doNotEnableEdgeToEdgeOnOlderSdk() { From 1883aa64326af9654d25b3a805f4e899cb07732b Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 31 Jan 2022 10:16:00 -0800 Subject: [PATCH 03/14] Make changes from CL --- .../io/flutter/app/FlutterActivityDelegate.java | 10 ++++++++++ .../embedding/android/FlutterActivity.java | 10 ++++++++++ .../android/FlutterFragmentActivity.java | 10 ++++++++++ .../flutter/plugin/platform/PlatformPlugin.java | 17 ++++++++++++++++- 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java index 2e435f872367c..fcb1cc4171241 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java @@ -24,6 +24,7 @@ import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager.LayoutParams; +import androidx.core.view.WindowCompat; import io.flutter.Log; import io.flutter.plugin.common.PluginRegistry; import io.flutter.plugin.platform.PlatformPlugin; @@ -143,6 +144,15 @@ public void onCreate(Bundle savedInstanceState) { window.setStatusBarColor(0x40000000); if (Build.VERSION.SDK_INT <= 19) { window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI_LEGACY); + } else { + WindowCompat.setDecorFitsSystemWindows(window, false); + if (Build.VERSION.SDK_INT < 30) { + window + .getDecorView() + .setSystemUiVisibility( + window.getDecorView().getSystemUiVisibility() + & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + } } } diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 0368f0103ab05..e5bc6c7cbeaff 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -38,6 +38,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.content.res.ResourcesCompat; +import androidx.core.view.WindowCompat; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; @@ -592,6 +593,15 @@ private void configureStatusBarForFullscreenFlutterExperience() { window.setStatusBarColor(0x40000000); if (Build.VERSION.SDK_INT <= 19) { window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI_LEGACY); + } else { + WindowCompat.setDecorFitsSystemWindows(window, false); + if (Build.VERSION.SDK_INT < 30) { + window + .getDecorView() + .setSystemUiVisibility( + window.getDecorView().getSystemUiVisibility() + & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + } } } } diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index 2fb1cb58691e4..a03707669ff87 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -38,6 +38,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.content.res.ResourcesCompat; +import androidx.core.view.WindowCompat; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import io.flutter.Log; @@ -497,6 +498,15 @@ private void configureStatusBarForFullscreenFlutterExperience() { window.setStatusBarColor(0x40000000); if (Build.VERSION.SDK_INT <= 19) { window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI_LEGACY); + } else { + WindowCompat.setDecorFitsSystemWindows(window, false); + if (Build.VERSION.SDK_INT < 30) { + window + .getDecorView() + .setSystemUiVisibility( + window.getDecorView().getSystemUiVisibility() + & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + } } } } diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index 719a94e482749..cc1aabd173d74 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -434,7 +434,22 @@ public void updateSystemUiOverlays() { } else if (Build.VERSION.SDK_INT <= 19) { // On API 19, if there is not a system ui mode already set, we wish to restore the enabled // flags to the default system ui flags defined above. - activity.getWindow().getDecorView().setSystemUiVisibility(DEFAULT_SYSTEM_UI_LEGACY); + if (Build.VERSION.SDK_INT <= 19) { + activity + .getWindow() + .getDecorView() + .setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI_LEGACY); + } else { + WindowCompat.setDecorFitsSystemWindows(activity.getWindow(), false); + if (Build.VERSION.SDK_INT < 30) { + activity + .getWindow() + .getDecorView() + .setSystemUiVisibility( + activity.getWindow().getDecorView().getSystemUiVisibility() + & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + } + } } } } From ae0dc091615d4686ef4cdb490c701b46ee2fa670 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 31 Jan 2022 13:30:04 -0800 Subject: [PATCH 04/14] Clean code --- .../flutter/app/FlutterActivityDelegate.java | 19 +- .../embedding/android/FlutterActivity.java | 18 +- .../android/FlutterFragmentActivity.java | 19 +- .../plugin/platform/PlatformPlugin.java | 167 ++++++++---------- .../plugin/platform/PlatformPluginTest.java | 4 + 5 files changed, 103 insertions(+), 124 deletions(-) diff --git a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java index fcb1cc4171241..2db05536970eb 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java @@ -27,7 +27,6 @@ import androidx.core.view.WindowCompat; import io.flutter.Log; import io.flutter.plugin.common.PluginRegistry; -import io.flutter.plugin.platform.PlatformPlugin; import io.flutter.util.Preconditions; import io.flutter.view.FlutterMain; import io.flutter.view.FlutterNativeView; @@ -142,17 +141,13 @@ public void onCreate(Bundle savedInstanceState) { Window window = activity.getWindow(); window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); - if (Build.VERSION.SDK_INT <= 19) { - window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI_LEGACY); - } else { - WindowCompat.setDecorFitsSystemWindows(window, false); - if (Build.VERSION.SDK_INT < 30) { - window - .getDecorView() - .setSystemUiVisibility( - window.getDecorView().getSystemUiVisibility() - & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - } + WindowCompat.setDecorFitsSystemWindows(window, false); + if (Build.VERSION.SDK_INT < 30) { + // This ensures that the navigation bar is not hidden for APIs 19-30, + // as dictated by the implementation of WindowCompat. + View view = window.getDecorView(); + view.setSystemUiVisibility( + view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); } } diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index e5bc6c7cbeaff..70c1d7b166c28 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -591,17 +591,13 @@ private void configureStatusBarForFullscreenFlutterExperience() { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); - if (Build.VERSION.SDK_INT <= 19) { - window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI_LEGACY); - } else { - WindowCompat.setDecorFitsSystemWindows(window, false); - if (Build.VERSION.SDK_INT < 30) { - window - .getDecorView() - .setSystemUiVisibility( - window.getDecorView().getSystemUiVisibility() - & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - } + WindowCompat.setDecorFitsSystemWindows(window, false); + if (Build.VERSION.SDK_INT < 30) { + // This ensures that the navigation bar is not hidden for APIs 19-30, + // as dictated by the implementation of WindowCompat. + View view = window.getDecorView(); + view.setSystemUiVisibility( + view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); } } } diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index a03707669ff87..ba2ab442a4d76 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -46,7 +46,6 @@ import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.FlutterShellArgs; import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister; -import io.flutter.plugin.platform.PlatformPlugin; import io.flutter.util.ViewUtils; /** @@ -496,17 +495,13 @@ private void configureStatusBarForFullscreenFlutterExperience() { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); - if (Build.VERSION.SDK_INT <= 19) { - window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI_LEGACY); - } else { - WindowCompat.setDecorFitsSystemWindows(window, false); - if (Build.VERSION.SDK_INT < 30) { - window - .getDecorView() - .setSystemUiVisibility( - window.getDecorView().getSystemUiVisibility() - & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - } + WindowCompat.setDecorFitsSystemWindows(window, false); + if (Build.VERSION.SDK_INT < 30) { + // This ensures that the navigation bar is not hidden for APIs 19-30, + // as dictated by the implementation of WindowCompat. + View view = window.getDecorView(); + view.setSystemUiVisibility( + view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); } } } diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index cc1aabd173d74..c68c541275d09 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -30,7 +30,7 @@ /** Android implementation of the platform plugin. */ public class PlatformPlugin { - // These flags are used on API 16-19 to define the default layout. + // These flags are used on API 19 to define the default layout. public static final int DEFAULT_SYSTEM_UI_LEGACY = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; @@ -239,50 +239,11 @@ public void onSystemUiVisibilityChange(int visibility) { }); } - private void setSystemChromeEnabledSystemUIModeLegacy(PlatformChannel.SystemUiMode systemUiMode) { - int enabledOverlays; - - if (systemUiMode == PlatformChannel.SystemUiMode.LEAN_BACK) { - // LEAN BACK - enabledOverlays = - 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; - } else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE - && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - // IMMERSIVE - enabledOverlays = - View.SYSTEM_UI_FLAG_IMMERSIVE - | 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; - } else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE_STICKY - && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - // STICKY IMMERSIVE - enabledOverlays = - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | 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; - } else { - // When none of the conditions are matched, return without updating the system UI overlays. - return; - } - - activity.getWindow().getDecorView().setSystemUiVisibility(enabledOverlays); - } - private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode systemUiMode) { - // As of API 30, controlling insets and overlays changed significantly, but provided backwards - // compatibility back through API 20. Flutter currently supports API 19, so the legacy code is - // left for that case. if (Build.VERSION.SDK_INT <= 19) { + // As of API 30, the Android APIs for overlays/insets provides backwards compatibility back + // through + // API 19. Since Flutter currently supports API 19, the legacy code is used for that case. setSystemChromeEnabledSystemUIModeLegacy(systemUiMode); } else { Window window = activity.getWindow(); @@ -346,41 +307,49 @@ private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode sys currentSystemUiMode = systemUiMode; } - private void setSystemChromeEnabledSystemUIOverlaysLegacy( - List overlaysToShow) { - int enabledOverlays = - DEFAULT_SYSTEM_UI_LEGACY - | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; - - // The SYSTEM_UI_FLAG_IMMERSIVE_STICKY flag was introduced in API 19, so we - // apply it if desired. - if (overlaysToShow.size() == 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - enabledOverlays |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - } + private void setSystemChromeEnabledSystemUIModeLegacy(PlatformChannel.SystemUiMode systemUiMode) { + int enabledOverlays; - for (int i = 0; i < overlaysToShow.size(); ++i) { - PlatformChannel.SystemUiOverlay overlayToShow = overlaysToShow.get(i); - switch (overlayToShow) { - case TOP_OVERLAYS: - enabledOverlays &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; - break; - case BOTTOM_OVERLAYS: - enabledOverlays &= ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; - enabledOverlays &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; - break; - } + if (systemUiMode == PlatformChannel.SystemUiMode.LEAN_BACK) { + // LEAN BACK + enabledOverlays = + 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; + } else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + // IMMERSIVE + enabledOverlays = + View.SYSTEM_UI_FLAG_IMMERSIVE + | 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; + } else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE_STICKY + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + // STICKY IMMERSIVE + enabledOverlays = + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | 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; + } else { + return; } - activity.getWindow().getDecorView().setSystemUiVisibility(enabledOverlays); } private void setSystemChromeEnabledSystemUIOverlays( List overlaysToShow) { if (Build.VERSION.SDK_INT <= 19) { - // This method calls offer support for the same behaviors below for API 19 that is unsupported - // by Android new overlays/insets API that offers backward compatibility only up to API 20. + // As of API 30, the Android APIs for overlays/insets provides backwards compatibility back + // through + // API 19. Since Flutter currently supports API 19, the legacy code is used for that case. setSystemChromeEnabledSystemUIOverlaysLegacy(overlaysToShow); } else { Window window = activity.getWindow(); @@ -396,7 +365,6 @@ private void setSystemChromeEnabledSystemUIOverlays( // We apply sticky immersive mode if desired. Available starting at SDK 20. if (overlaysToShow.size() == 0) { currentSystemUiMode = PlatformChannel.SystemUiMode.IMMERSIVE_STICKY; - windowInsetsControllerCompat.setSystemBarsBehavior( WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); } @@ -417,6 +385,36 @@ private void setSystemChromeEnabledSystemUIOverlays( currentOverlays = overlaysToShow; } + private void setSystemChromeEnabledSystemUIOverlaysLegacy( + List overlaysToShow) { + int enabledOverlays = + DEFAULT_SYSTEM_UI_LEGACY + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + + // The SYSTEM_UI_FLAG_IMMERSIVE_STICKY flag was introduced in API 19, so we + // apply it if desired. + if (overlaysToShow.size() == 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + enabledOverlays |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + } + + for (int i = 0; i < overlaysToShow.size(); ++i) { + PlatformChannel.SystemUiOverlay overlayToShow = overlaysToShow.get(i); + switch (overlayToShow) { + case TOP_OVERLAYS: + enabledOverlays &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; + break; + case BOTTOM_OVERLAYS: + enabledOverlays &= ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + enabledOverlays &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + break; + } + } + + activity.getWindow().getDecorView().setSystemUiVisibility(enabledOverlays); + } + /** * Refreshes Android's window system UI (AKA system chrome) to match Flutter's desired {@link * PlatformChannel.SystemChromeStyle}. @@ -431,24 +429,15 @@ public void updateSystemUiOverlays() { } else { if (currentSystemUiMode != null) { setSystemChromeEnabledSystemUIMode(currentSystemUiMode); - } else if (Build.VERSION.SDK_INT <= 19) { - // On API 19, if there is not a system ui mode already set, we wish to restore the enabled - // flags to the default system ui flags defined above. - if (Build.VERSION.SDK_INT <= 19) { - activity - .getWindow() - .getDecorView() - .setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI_LEGACY); - } else { - WindowCompat.setDecorFitsSystemWindows(activity.getWindow(), false); - if (Build.VERSION.SDK_INT < 30) { - activity - .getWindow() - .getDecorView() - .setSystemUiVisibility( - activity.getWindow().getDecorView().getSystemUiVisibility() - & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - } + } else { + Window window = activity.getWindow(); + WindowCompat.setDecorFitsSystemWindows(window, false); + if (Build.VERSION.SDK_INT < 30) { + // This ensures that the navigation bar is not hidden for APIs 19-30, + // as dictated by the implementation of WindowCompat. + View view = window.getDecorView(); + view.setSystemUiVisibility( + view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); } } } diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java index bb9f71214a05e..eddca1535c033 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -319,6 +319,8 @@ public void setStatusBarIconBrightness() { @Config(sdk = 29) @Test public void setSystemUiModeLegacy() { + // This test reflects the behavior under the hood of the Android overlay/inset APIs used for API + // 20-29 in the plugin. View fakeDecorView = mock(View.class); Window fakeWindow = mock(Window.class); when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); @@ -417,6 +419,8 @@ public void setSystemUiMode() { @Config(sdk = 29) @Test public void showSystemOverlaysLegacy() { + // This test reflects the behavior under the hood of the Android overlay/inset APIs used for API + // 20-29 in the plugin. View fakeDecorView = mock(View.class); Window fakeWindow = mock(Window.class); when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); From c64012b4a9ff9b6a85643c831fd2b39abfce8aa4 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 31 Jan 2022 14:10:54 -0800 Subject: [PATCH 05/14] Add comments --- .../plugin/platform/PlatformPlugin.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index c68c541275d09..a1690c8b6cafb 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -242,8 +242,8 @@ public void onSystemUiVisibilityChange(int visibility) { private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode systemUiMode) { if (Build.VERSION.SDK_INT <= 19) { // As of API 30, the Android APIs for overlays/insets provides backwards compatibility back - // through - // API 19. Since Flutter currently supports API 19, the legacy code is used for that case. + // through API 19. Since Flutter currently supports API 19, the legacy code is used for + // that case. setSystemChromeEnabledSystemUIModeLegacy(systemUiMode); } else { Window window = activity.getWindow(); @@ -253,8 +253,7 @@ private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode sys if (systemUiMode == PlatformChannel.SystemUiMode.LEAN_BACK) { // LEAN BACK - // Available starting at SDK 20, due to the backwards compatibility provided by the - // WindowInsetsControllerCompat class for setting the behavior of system bars. + // Available starting at SDK 16. Implemented for API 20+ here. // Should not show overlays, tap to reveal overlays, needs onChange callback // When the overlays come in on tap, the app does not receive the gesture and does not know // the system overlay has changed. The overlays cannot be dismissed, so adding the callback @@ -266,8 +265,7 @@ private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode sys WindowCompat.setDecorFitsSystemWindows(window, false); } else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE) { // IMMERSIVE - // Available starting at SDK 20, due to the backwards compatibility provided by the - // WindowInsetsControllerCompat class for setting the behavior of system bars. + // Available starting at SDK 19. Implemented for API 20+ here. // Should not show overlays, swipe from edges to reveal overlays, needs onChange callback // When the overlays come in on swipe, the app does not receive the gesture and does not // know @@ -280,8 +278,7 @@ private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode sys WindowCompat.setDecorFitsSystemWindows(window, false); } else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE_STICKY) { // STICKY IMMERSIVE - // Available starting at SDK 20, due to the backwards compatibility provided by the - // WindowInsetsControllerCompat class for setting the behavior of system bars. + // Available starting at SDK 19. Implemented for API 20+ here. // Should not show overlays, swipe from edges to reveal overlays. The app will also receive // the swipe gesture. The overlays cannot be dismissed, so adding callback support will // allow users to restore the system ui and dismiss the overlays. @@ -312,6 +309,7 @@ private void setSystemChromeEnabledSystemUIModeLegacy(PlatformChannel.SystemUiMo if (systemUiMode == PlatformChannel.SystemUiMode.LEAN_BACK) { // LEAN BACK + // Available starting at SDK 16. Implemented for APIs 16-19 here. enabledOverlays = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION @@ -321,6 +319,8 @@ private void setSystemChromeEnabledSystemUIModeLegacy(PlatformChannel.SystemUiMo } else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // IMMERSIVE + // Available starting at SDK 19. Implemented for API 19 here. Earlier versions will not be + // affected by this. enabledOverlays = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE @@ -331,6 +331,8 @@ private void setSystemChromeEnabledSystemUIModeLegacy(PlatformChannel.SystemUiMo } else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE_STICKY && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // STICKY IMMERSIVE + // Available starting at SDK 19. Implemented for API 19 here. Earlier versions will not be + // affected by this. enabledOverlays = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_STABLE @@ -348,8 +350,8 @@ private void setSystemChromeEnabledSystemUIOverlays( List overlaysToShow) { if (Build.VERSION.SDK_INT <= 19) { // As of API 30, the Android APIs for overlays/insets provides backwards compatibility back - // through - // API 19. Since Flutter currently supports API 19, the legacy code is used for that case. + // through API 19. Since Flutter currently supports API 19, the legacy code is used + // for that case. setSystemChromeEnabledSystemUIOverlaysLegacy(overlaysToShow); } else { Window window = activity.getWindow(); @@ -396,6 +398,7 @@ private void setSystemChromeEnabledSystemUIOverlaysLegacy( // The SYSTEM_UI_FLAG_IMMERSIVE_STICKY flag was introduced in API 19, so we // apply it if desired. if (overlaysToShow.size() == 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + currentSystemUiMode = PlatformChannel.SystemUiMode.IMMERSIVE_STICKY; enabledOverlays |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; } @@ -411,7 +414,6 @@ private void setSystemChromeEnabledSystemUIOverlaysLegacy( break; } } - activity.getWindow().getDecorView().setSystemUiVisibility(enabledOverlays); } From bec1716778e963fef8efd77758833745030283e9 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 31 Jan 2022 14:45:56 -0800 Subject: [PATCH 06/14] Remove legacy flag --- .../android/io/flutter/plugin/platform/PlatformPlugin.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index a1690c8b6cafb..e134f59984beb 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -30,9 +30,6 @@ /** Android implementation of the platform plugin. */ public class PlatformPlugin { - // These flags are used on API 19 to define the default layout. - public static final int DEFAULT_SYSTEM_UI_LEGACY = - View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; private final Activity activity; private final PlatformChannel platformChannel; @@ -390,7 +387,8 @@ private void setSystemChromeEnabledSystemUIOverlays( private void setSystemChromeEnabledSystemUIOverlaysLegacy( List overlaysToShow) { int enabledOverlays = - DEFAULT_SYSTEM_UI_LEGACY + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; From b977e5c3601901d647dbb023ba838c998bbd0a0d Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 31 Jan 2022 14:59:51 -0800 Subject: [PATCH 07/14] Correct check --- .../plugin/platform/PlatformPlugin.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index e134f59984beb..f38c2dec3995a 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -426,18 +426,20 @@ private void setSystemChromeEnabledSystemUIOverlaysLegacy( public void updateSystemUiOverlays() { if (currentOverlays != null) { setSystemChromeEnabledSystemUIOverlays(currentOverlays); - } else { - if (currentSystemUiMode != null) { - setSystemChromeEnabledSystemUIMode(currentSystemUiMode); - } else { - Window window = activity.getWindow(); - WindowCompat.setDecorFitsSystemWindows(window, false); - if (Build.VERSION.SDK_INT < 30) { - // This ensures that the navigation bar is not hidden for APIs 19-30, - // as dictated by the implementation of WindowCompat. - View view = window.getDecorView(); - view.setSystemUiVisibility( - view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + + if (currentOverlays.size() > 0) { + if (currentSystemUiMode != null) { + setSystemChromeEnabledSystemUIMode(currentSystemUiMode); + } else { + Window window = activity.getWindow(); + WindowCompat.setDecorFitsSystemWindows(window, false); + if (Build.VERSION.SDK_INT < 30) { + // This ensures that the navigation bar is not hidden for APIs 19-30, + // as dictated by the implementation of WindowCompat. + View view = window.getDecorView(); + view.setSystemUiVisibility( + view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + } } } } From 564b14d36c994472270c59393a97f1a751136ec4 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Tue, 1 Feb 2022 09:42:01 -0800 Subject: [PATCH 08/14] Formatting --- .../android/io/flutter/plugin/platform/PlatformPlugin.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index f38c2dec3995a..da555e22eecfc 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -265,8 +265,7 @@ private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode sys // Available starting at SDK 19. Implemented for API 20+ here. // Should not show overlays, swipe from edges to reveal overlays, needs onChange callback // When the overlays come in on swipe, the app does not receive the gesture and does not - // know - // the system overlay has changed. The overlays cannot be dismissed, so adding callback + // know the system overlay has changed. The overlays cannot be dismissed, so adding callback // support will allow users to restore the system ui and dismiss the overlays. // Not compatible with top/bottom overlays enabled. windowInsetsControllerCompat.setSystemBarsBehavior( From 2a7e2c5adfa75f32de6472adedb66a918025031e Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 3 Feb 2022 13:24:10 -0800 Subject: [PATCH 09/14] Clarify comments --- .../android/io/flutter/app/FlutterActivityDelegate.java | 2 +- .../android/io/flutter/embedding/android/FlutterActivity.java | 2 +- .../io/flutter/embedding/android/FlutterFragmentActivity.java | 2 +- .../android/io/flutter/plugin/platform/PlatformPlugin.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java index 2db05536970eb..d0bd885197dda 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java @@ -143,7 +143,7 @@ public void onCreate(Bundle savedInstanceState) { window.setStatusBarColor(0x40000000); WindowCompat.setDecorFitsSystemWindows(window, false); if (Build.VERSION.SDK_INT < 30) { - // This ensures that the navigation bar is not hidden for APIs 19-30, + // This ensures that the navigation bar is not hidden for APIs < 30, // as dictated by the implementation of WindowCompat. View view = window.getDecorView(); view.setSystemUiVisibility( diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 70c1d7b166c28..36567d90904f8 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -593,7 +593,7 @@ private void configureStatusBarForFullscreenFlutterExperience() { window.setStatusBarColor(0x40000000); WindowCompat.setDecorFitsSystemWindows(window, false); if (Build.VERSION.SDK_INT < 30) { - // This ensures that the navigation bar is not hidden for APIs 19-30, + // This ensures that the navigation bar is not hidden for APIs < 30, // as dictated by the implementation of WindowCompat. View view = window.getDecorView(); view.setSystemUiVisibility( diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index ba2ab442a4d76..50382659ed370 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -497,7 +497,7 @@ private void configureStatusBarForFullscreenFlutterExperience() { window.setStatusBarColor(0x40000000); WindowCompat.setDecorFitsSystemWindows(window, false); if (Build.VERSION.SDK_INT < 30) { - // This ensures that the navigation bar is not hidden for APIs 19-30, + // This ensures that the navigation bar is not hidden for APIs < 30, // as dictated by the implementation of WindowCompat. View view = window.getDecorView(); view.setSystemUiVisibility( diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index da555e22eecfc..25c65d1508d0b 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -433,7 +433,7 @@ public void updateSystemUiOverlays() { Window window = activity.getWindow(); WindowCompat.setDecorFitsSystemWindows(window, false); if (Build.VERSION.SDK_INT < 30) { - // This ensures that the navigation bar is not hidden for APIs 19-30, + // This ensures that the navigation bar is not hidden for APIs < 30, // as dictated by the implementation of WindowCompat. View view = window.getDecorView(); view.setSystemUiVisibility( From fe079cff36adafafe0e7c26b679f1240378b010b Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 14 Mar 2022 11:29:15 -0700 Subject: [PATCH 10/14] Remove changes for API 30+ to test --- .../android/io/flutter/app/FlutterActivityDelegate.java | 2 +- .../android/io/flutter/embedding/android/FlutterActivity.java | 2 +- .../io/flutter/embedding/android/FlutterFragmentActivity.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java index d0bd885197dda..3d56c71521f90 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java @@ -141,8 +141,8 @@ public void onCreate(Bundle savedInstanceState) { Window window = activity.getWindow(); window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); - WindowCompat.setDecorFitsSystemWindows(window, false); if (Build.VERSION.SDK_INT < 30) { + WindowCompat.setDecorFitsSystemWindows(window, false); // This ensures that the navigation bar is not hidden for APIs < 30, // as dictated by the implementation of WindowCompat. View view = window.getDecorView(); diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 9b6b6eed181d1..cef0c8df12dc9 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -591,8 +591,8 @@ private void configureStatusBarForFullscreenFlutterExperience() { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); - WindowCompat.setDecorFitsSystemWindows(window, false); if (Build.VERSION.SDK_INT < 30) { + WindowCompat.setDecorFitsSystemWindows(window, false); // This ensures that the navigation bar is not hidden for APIs < 30, // as dictated by the implementation of WindowCompat. View view = window.getDecorView(); diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index 50382659ed370..d03e99dd7822d 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -495,8 +495,8 @@ private void configureStatusBarForFullscreenFlutterExperience() { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); - WindowCompat.setDecorFitsSystemWindows(window, false); if (Build.VERSION.SDK_INT < 30) { + WindowCompat.setDecorFitsSystemWindows(window, false); // This ensures that the navigation bar is not hidden for APIs < 30, // as dictated by the implementation of WindowCompat. View view = window.getDecorView(); From 79f72601e97af9fc1cfbb0439ef181d3c25daf36 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 26 May 2022 13:56:10 -0700 Subject: [PATCH 11/14] Revert back to using default system ui flag --- .../io/flutter/app/FlutterActivityDelegate.java | 11 ++--------- .../io/flutter/embedding/android/FlutterActivity.java | 11 ++--------- .../embedding/android/FlutterFragmentActivity.java | 11 ++--------- 3 files changed, 6 insertions(+), 27 deletions(-) diff --git a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java index 3d56c71521f90..907022925a12e 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java @@ -24,7 +24,6 @@ import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager.LayoutParams; -import androidx.core.view.WindowCompat; import io.flutter.Log; import io.flutter.plugin.common.PluginRegistry; import io.flutter.util.Preconditions; @@ -136,19 +135,13 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent data) { } @Override + @SuppressWarnings("deprecation") public void onCreate(Bundle savedInstanceState) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = activity.getWindow(); window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); - if (Build.VERSION.SDK_INT < 30) { - WindowCompat.setDecorFitsSystemWindows(window, false); - // This ensures that the navigation bar is not hidden for APIs < 30, - // as dictated by the implementation of WindowCompat. - View view = window.getDecorView(); - view.setSystemUiVisibility( - view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - } + window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI); } String[] args = getArgsFromIntent(activity.getIntent()); diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index f34c4831adfa8..89258a7068716 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -39,7 +39,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.content.res.ResourcesCompat; -import androidx.core.view.WindowCompat; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; @@ -615,19 +614,13 @@ private View createFlutterView() { /*shouldDelayFirstAndroidViewDraw=*/ getRenderMode() == RenderMode.surface); } + @SuppressWarnings("deprecation") private void configureStatusBarForFullscreenFlutterExperience() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); - if (Build.VERSION.SDK_INT < 30) { - WindowCompat.setDecorFitsSystemWindows(window, false); - // This ensures that the navigation bar is not hidden for APIs < 30, - // as dictated by the implementation of WindowCompat. - View view = window.getDecorView(); - view.setSystemUiVisibility( - view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - } + window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI); } } diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index 0577a53c39f48..42a64b8bdd1b9 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -39,7 +39,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.content.res.ResourcesCompat; -import androidx.core.view.WindowCompat; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import io.flutter.Log; @@ -515,19 +514,13 @@ protected FlutterFragment createFlutterFragment() { } } + @SuppressWarnings("deprecation") private void configureStatusBarForFullscreenFlutterExperience() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); - if (Build.VERSION.SDK_INT < 30) { - WindowCompat.setDecorFitsSystemWindows(window, false); - // This ensures that the navigation bar is not hidden for APIs < 30, - // as dictated by the implementation of WindowCompat. - View view = window.getDecorView(); - view.setSystemUiVisibility( - view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - } + window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI); } } From 90407122291e9ea3ccc47fbbe7b948dbaa72cf2b Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 26 May 2022 14:17:48 -0700 Subject: [PATCH 12/14] Add imports, suppress warnings --- .../android/io/flutter/app/FlutterActivityDelegate.java | 1 + .../flutter/embedding/android/FlutterFragmentActivity.java | 1 + .../android/io/flutter/plugin/platform/PlatformPlugin.java | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java index 907022925a12e..211cff18b61a1 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java @@ -25,6 +25,7 @@ import android.view.Window; import android.view.WindowManager.LayoutParams; import io.flutter.Log; +import io.flutter.plugin.platform.PlatformPlugin; import io.flutter.plugin.common.PluginRegistry; import io.flutter.util.Preconditions; import io.flutter.view.FlutterMain; diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index 42a64b8bdd1b9..4250a206b5f72 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -46,6 +46,7 @@ import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.FlutterShellArgs; import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister; +import io.flutter.plugin.platform.PlatformPlugin; import io.flutter.util.ViewUtils; import java.util.ArrayList; import java.util.List; diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index dee9c80b3a3b1..f5c1b0fc40fde 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -30,6 +30,8 @@ /** Android implementation of the platform plugin. */ public class PlatformPlugin { + @SuppressWarnings("deprecation") + public static final int DEFAULT_SYSTEM_UI = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; private final Activity activity; private final PlatformChannel platformChannel; @@ -303,6 +305,7 @@ private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode sys currentSystemUiMode = systemUiMode; } + @SuppressWarnings("deprecation") private void setSystemChromeEnabledSystemUIModeLegacy(PlatformChannel.SystemUiMode systemUiMode) { int enabledOverlays; @@ -386,6 +389,7 @@ private void setSystemChromeEnabledSystemUIOverlays( currentOverlays = overlaysToShow; } + @SuppressWarnings("deprecation") private void setSystemChromeEnabledSystemUIOverlaysLegacy( List overlaysToShow) { int enabledOverlays = @@ -425,6 +429,7 @@ private void setSystemChromeEnabledSystemUIOverlaysLegacy( * Window} associated with the {@link android.app.Activity} that was provided to this {@code * PlatformPlugin}. */ + @SuppressWarnings("deprecation") public void updateSystemUiOverlays() { if (currentOverlays != null) { setSystemChromeEnabledSystemUIOverlays(currentOverlays); From c1c5d409c9bc6ef2667b88085dd6fb28a7fb9deb Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 26 May 2022 14:18:12 -0700 Subject: [PATCH 13/14] Formatting --- .../android/io/flutter/app/FlutterActivityDelegate.java | 2 +- .../android/io/flutter/plugin/platform/PlatformPlugin.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java index 211cff18b61a1..846fb97083628 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java @@ -25,8 +25,8 @@ import android.view.Window; import android.view.WindowManager.LayoutParams; import io.flutter.Log; -import io.flutter.plugin.platform.PlatformPlugin; import io.flutter.plugin.common.PluginRegistry; +import io.flutter.plugin.platform.PlatformPlugin; import io.flutter.util.Preconditions; import io.flutter.view.FlutterMain; import io.flutter.view.FlutterNativeView; diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index f5c1b0fc40fde..d53bae3ac90b6 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -31,7 +31,8 @@ /** Android implementation of the platform plugin. */ public class PlatformPlugin { @SuppressWarnings("deprecation") - public static final int DEFAULT_SYSTEM_UI = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + public static final int DEFAULT_SYSTEM_UI = + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; private final Activity activity; private final PlatformChannel platformChannel; From dd9d66c73d692b98555ca4570dcca73a2f877e73 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 26 May 2022 15:05:22 -0700 Subject: [PATCH 14/14] Undo unecessary deletion: --- .../plugin/platform/PlatformPlugin.java | 3 ++ .../plugin/platform/PlatformPluginTest.java | 39 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index d53bae3ac90b6..8439c8ff3ed23 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -451,6 +451,9 @@ public void updateSystemUiOverlays() { } } } + if (currentTheme != null) { + setSystemChromeSystemUIOverlayStyle(currentTheme); + } } private void restoreSystemChromeSystemUIOverlays() { diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java index e74565c3129e3..9c9d2d3ffbe9a 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -6,6 +6,8 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -506,6 +508,43 @@ public void showSystemOverlays() { verify(fakeWindowInsetsController).show(WindowInsetsCompat.Type.navigationBars()); } + @Config(sdk = 30) + @Test + public void verifyUpdateSystemUiOverlaysAppliesCurrentTheme() { + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + Activity fakeActivity = mock(Activity.class); + when(fakeActivity.getWindow()).thenReturn(fakeWindow); + PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); + PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); + WindowInsetsController fakeWindowInsetsController = mock(WindowInsetsController.class); + when(fakeWindow.getInsetsController()).thenReturn(fakeWindowInsetsController); + + // Style that requires usage of all system bar APIs used in PlatformPlugin to update overlay + // style + SystemChromeStyle testStyle = + new SystemChromeStyle( + 0XFF000000, Brightness.LIGHT, true, 0XFFC70039, Brightness.LIGHT, 0XFF006DB3, true); + + platformPlugin.updateSystemUiOverlays(); + + verify(fakeWindow, never()).setStatusBarColor(anyInt()); + verify(fakeWindow, never()).setNavigationBarColor(anyInt()); + verify(fakeWindow, never()).setNavigationBarDividerColor(anyInt()); + verify(fakeWindow, never()).setStatusBarContrastEnforced(anyBoolean()); + verify(fakeWindow, never()).setNavigationBarContrastEnforced(anyBoolean()); + + platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(testStyle); + platformPlugin.updateSystemUiOverlays(); + + verify(fakeWindow, times(2)).setStatusBarColor(0xFF000000); + verify(fakeWindow, times(2)).setNavigationBarColor(0XFFC70039); + verify(fakeWindow, times(2)).setNavigationBarDividerColor(0XFF006DB3); + verify(fakeWindow, times(2)).setStatusBarContrastEnforced(true); + verify(fakeWindow, times(2)).setNavigationBarContrastEnforced(true); + } + @Config(sdk = 28) @Test public void doNotEnableEdgeToEdgeOnOlderSdk() {