[Engine] Support for Android Fullscreen Modes (flutter/engine#25785)

This commit is contained in:
Kate Lovett 2021-06-24 12:56:01 -05:00 committed by GitHub
parent 2a6abe950b
commit ea1ff8ab3b
4 changed files with 391 additions and 56 deletions

View File

@ -14,6 +14,7 @@ import io.flutter.plugin.common.JSONMethodCodec;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
@ -102,6 +103,22 @@ public class PlatformChannel {
result.error("error", exception.getMessage(), null);
}
break;
case "SystemChrome.setEnabledSystemUIMode":
try {
SystemUiMode mode = decodeSystemUiMode((String) arguments);
platformMessageHandler.showSystemUiMode(mode);
result.success(null);
} catch (JSONException | NoSuchFieldException exception) {
// JSONException: One or more expected fields were either omitted or referenced an
// invalid type.
// NoSuchFieldException: One or more of the overlay names are invalid.
result.error("error", exception.getMessage(), null);
}
break;
case "SystemChrome.setSystemUIChangeListener":
platformMessageHandler.setSystemUiChangeListener();
result.success(null);
break;
case "SystemChrome.restoreSystemUIOverlays":
platformMessageHandler.restoreSystemUiOverlays();
result.success(null);
@ -194,6 +211,12 @@ public class PlatformChannel {
this.platformMessageHandler = platformMessageHandler;
}
/** Informs Flutter of a change in the SystemUI overlays. */
public void systemChromeChanged(boolean overlaysAreVisible) {
Log.v(TAG, "Sending 'systemUIChange' message.");
channel.invokeMethod("SystemChrome.systemUIChange", Arrays.asList(overlaysAreVisible));
}
// TODO(mattcarroll): add support for IntDef annotations, then add @ScreenOrientation
/**
@ -313,6 +336,32 @@ public class PlatformChannel {
return overlays;
}
/**
* Decodes an object of JSON-encoded mode to a {@link SystemUiMode}.
*
* @throws JSONException if {@code encodedSystemUiMode} does not contain expected keys and value
* types.
* @throws NoSuchFieldException if any of the given encoded mode name is invalid.
*/
@NonNull
private SystemUiMode decodeSystemUiMode(@NonNull String encodedSystemUiMode)
throws JSONException, NoSuchFieldException {
SystemUiMode mode = SystemUiMode.fromValue(encodedSystemUiMode);
switch (mode) {
case LEAN_BACK:
return SystemUiMode.LEAN_BACK;
case IMMERSIVE:
return SystemUiMode.IMMERSIVE;
case IMMERSIVE_STICKY:
return SystemUiMode.IMMERSIVE_STICKY;
case EDGE_TO_EDGE:
return SystemUiMode.EDGE_TO_EDGE;
}
// Execution should never ever get this far, but if it does, we default to edge to edge.
return SystemUiMode.EDGE_TO_EDGE;
}
/**
* Decodes a JSON-encoded {@code encodedStyle} to a {@link SystemChromeStyle}.
*
@ -322,22 +371,19 @@ public class PlatformChannel {
@NonNull
private SystemChromeStyle decodeSystemChromeStyle(@NonNull JSONObject encodedStyle)
throws JSONException, NoSuchFieldException {
Brightness systemNavigationBarIconBrightness = null;
// TODO(mattcarroll): add color annotation
Integer systemNavigationBarColor = null;
// TODO(mattcarroll): add color annotation
Integer systemNavigationBarDividerColor = null;
Brightness statusBarIconBrightness = null;
// TODO(mattcarroll): add color annotation
Integer statusBarColor = null;
Brightness statusBarIconBrightness = null;
boolean systemStatusBarContrastEnforced = true;
// TODO(mattcarroll): add color annotation
Integer systemNavigationBarColor = null;
Brightness systemNavigationBarIconBrightness = null;
// TODO(mattcarroll): add color annotation
Integer systemNavigationBarDividerColor = null;
boolean systemNavigationBarContrastEnforced = true;
if (!encodedStyle.isNull("systemNavigationBarIconBrightness")) {
systemNavigationBarIconBrightness =
Brightness.fromValue(encodedStyle.getString("systemNavigationBarIconBrightness"));
}
if (!encodedStyle.isNull("systemNavigationBarColor")) {
systemNavigationBarColor = encodedStyle.getInt("systemNavigationBarColor");
if (!encodedStyle.isNull("statusBarColor")) {
statusBarColor = encodedStyle.getInt("statusBarColor");
}
if (!encodedStyle.isNull("statusBarIconBrightness")) {
@ -345,20 +391,36 @@ public class PlatformChannel {
Brightness.fromValue(encodedStyle.getString("statusBarIconBrightness"));
}
if (!encodedStyle.isNull("statusBarColor")) {
statusBarColor = encodedStyle.getInt("statusBarColor");
if (!encodedStyle.isNull("systemStatusBarContrastEnforced")) {
systemStatusBarContrastEnforced = encodedStyle.getBoolean("systemStatusBarContrastEnforced");
}
if (!encodedStyle.isNull("systemNavigationBarColor")) {
systemNavigationBarColor = encodedStyle.getInt("systemNavigationBarColor");
}
if (!encodedStyle.isNull("systemNavigationBarIconBrightness")) {
systemNavigationBarIconBrightness =
Brightness.fromValue(encodedStyle.getString("systemNavigationBarIconBrightness"));
}
if (!encodedStyle.isNull("systemNavigationBarDividerColor")) {
systemNavigationBarDividerColor = encodedStyle.getInt("systemNavigationBarDividerColor");
}
if (!encodedStyle.isNull("systemNavigationBarContrastEnforced")) {
systemNavigationBarContrastEnforced =
encodedStyle.getBoolean("systemNavigationBarContrastEnforced");
}
return new SystemChromeStyle(
statusBarColor,
statusBarIconBrightness,
systemStatusBarContrastEnforced,
systemNavigationBarColor,
systemNavigationBarIconBrightness,
systemNavigationBarDividerColor);
systemNavigationBarDividerColor,
systemNavigationBarContrastEnforced);
}
/**
@ -400,11 +462,42 @@ public class PlatformChannel {
void showSystemOverlays(@NonNull List<SystemUiOverlay> overlays);
/**
* The Flutter application would like to restore the visibility of system overlays to the last
* set of overlays sent via {@link #showSystemOverlays(List)}.
* The Flutter application would like the Android system to display the given {@code mode}.
*
* <p>If {@link #showSystemOverlays(List)} has yet to be called, then a default system overlay
* appearance is desired:
* <p>{@link SystemUiMode#LEAN_BACK} refers to a fullscreen experience that restores system bars
* upon tapping anywhere in the application. This tap gesture is not received by the
* application.
*
* <p>{@link SystemUiMode#IMMERSIVE} refers to a fullscreen experience that restores system bars
* upon swiping from the edge of the viewport. This swipe gesture is not recived by the
* application.
*
* <p>{@link SystemUiMode#IMMERSIVE_STICKY} refers to a fullscreen experience that restores
* system bars upon swiping from the edge of the viewport. This swipe gesture is received by the
* application, in contrast to {@link SystemUiMode#IMMERSIVE}.
*
* <p>{@link SystemUiMode#EDGE_TO_EDGE} refers to a layout configuration that will consume the
* full viewport. This full screen experience does not hide status bars. These status bars can
* be set to transparent, making the buttons and icons hover over the fullscreen application.
*/
void showSystemUiMode(@NonNull SystemUiMode mode);
/**
* The Flutter application would like the Android system to notify the framework when the system
* ui visibility has changed.
*
* <p>This is relevant when using {@link SystemUiMode}s for fullscreen applications, from which
* the system overlays can appear or disappear based on user input.
*/
void setSystemUiChangeListener();
/**
* The Flutter application would like to restore the visibility of system overlays to the last
* set of overlays sent via {@link #showSystemOverlays(List)} or {@link
* #showSystemUiMode(SystemUiMode)}.
*
* <p>If {@link #showSystemOverlays(List)} or {@link #showSystemUiMode(SystemUiMode)} has yet to
* be called, then a default system overlay appearance is desired:
*
* <p>{@code View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN }
*/
@ -542,6 +635,35 @@ public class PlatformChannel {
}
}
/** The set of Android system fullscreen modes as perceived by the Flutter application. */
public enum SystemUiMode {
LEAN_BACK("SystemUiMode.leanBack"),
IMMERSIVE("SystemUiMode.immersive"),
IMMERSIVE_STICKY("SystemUiMode.immersiveSticky"),
EDGE_TO_EDGE("SystemUiMode.edgeToEdge");
/**
* Returns the SystemUiMode for the provied encoded value. @throws NoSuchFieldException if any
* of the given encoded overlay names are invalid.
*/
@NonNull
static SystemUiMode fromValue(@NonNull String encodedName) throws NoSuchFieldException {
for (SystemUiMode mode : SystemUiMode.values()) {
if (mode.encodedName.equals(encodedName)) {
return mode;
}
}
throw new NoSuchFieldException("No such SystemUiMode: " + encodedName);
}
@NonNull private String encodedName;
/** Returens the encoded {@link SystemUiMode} */
SystemUiMode(@NonNull String encodedName) {
this.encodedName = encodedName;
}
}
/**
* The color and label of an application that appears in Android's app switcher, AKA recents
* screen.
@ -562,23 +684,29 @@ public class PlatformChannel {
// TODO(mattcarroll): add color annotation
@Nullable public final Integer statusBarColor;
@Nullable public final Brightness statusBarIconBrightness;
@Nullable public final boolean systemStatusBarContrastEnforced;
// TODO(mattcarroll): add color annotation
@Nullable public final Integer systemNavigationBarColor;
@Nullable public final Brightness systemNavigationBarIconBrightness;
// TODO(mattcarroll): add color annotation
@Nullable public final Integer systemNavigationBarDividerColor;
@Nullable public final boolean systemNavigationBarContrastEnforced;
public SystemChromeStyle(
@Nullable Integer statusBarColor,
@Nullable Brightness statusBarIconBrightness,
@Nullable boolean systemStatusBarContrastEnforced,
@Nullable Integer systemNavigationBarColor,
@Nullable Brightness systemNavigationBarIconBrightness,
@Nullable Integer systemNavigationBarDividerColor) {
@Nullable Integer systemNavigationBarDividerColor,
@Nullable boolean systemNavigationBarContrastEnforced) {
this.statusBarColor = statusBarColor;
this.statusBarIconBrightness = statusBarIconBrightness;
this.systemStatusBarContrastEnforced = systemStatusBarContrastEnforced;
this.systemNavigationBarColor = systemNavigationBarColor;
this.systemNavigationBarIconBrightness = systemNavigationBarIconBrightness;
this.systemNavigationBarDividerColor = systemNavigationBarDividerColor;
this.systemNavigationBarContrastEnforced = systemNavigationBarContrastEnforced;
}
}

View File

@ -85,6 +85,16 @@ public class PlatformPlugin {
setSystemChromeEnabledSystemUIOverlays(overlays);
}
@Override
public void showSystemUiMode(@NonNull PlatformChannel.SystemUiMode mode) {
setSystemChromeEnabledSystemUIMode(mode);
}
@Override
public void setSystemUiChangeListener() {
setSystemChromeChangeListener();
}
@Override
public void restoreSystemUiOverlays() {
restoreSystemChromeSystemUIOverlays();
@ -204,6 +214,101 @@ public class PlatformPlugin {
}
}
private void setSystemChromeChangeListener() {
// Set up a listener to notify the framework when the system ui has changed.
View decorView = activity.getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(
new View.OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(int visibility) {
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
// The system bars are visible. Make any desired adjustments to
// your UI, such as showing the action bar or other navigational
// controls. Another common action is to set a timer to dismiss
// the system bars and restore the fullscreen mode that was
// previously enabled.
platformChannel.systemChromeChanged(false);
} else {
// The system bars are NOT visible. Make any desired adjustments
// to your UI, such as hiding the action bar or other
// navigational controls.
platformChannel.systemChromeChanged(true);
}
}
});
}
private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode systemUiMode) {
int enabledOverlays =
DEFAULT_SYSTEM_UI
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
if (systemUiMode == PlatformChannel.SystemUiMode.LEAN_BACK
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
// 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 recieve 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
| 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
// 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
| 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
// 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
| 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.EDGE_TO_EDGE
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
// EDGE TO EDGE
// Available starting at 16
// SDK 29 and up will apply a translucent body scrim behind 2/3 button navigation bars
// to ensure contrast with buttons on the nav bar.
// SDK 28 and lower will support a transparent 2/3 button navigation bar.
// Overlays should be included and not removed.
enabledOverlays =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
}
mEnabledOverlays = enabledOverlays;
updateSystemUiOverlays();
}
private void setSystemChromeEnabledSystemUIOverlays(
List<PlatformChannel.SystemUiOverlay> overlaysToShow) {
// Start by assuming we want to hide all system overlays (like an immersive
@ -263,49 +368,73 @@ public class PlatformPlugin {
Window window = activity.getWindow();
View view = window.getDecorView();
int flags = view.getSystemUiVisibility();
// You can change the navigation bar color (including translucent colors)
// in Android, but you can't change the color of the navigation buttons until
// Android O.
// LIGHT vs DARK effectively isn't supported until then.
// Build.VERSION_CODES.O
if (Build.VERSION.SDK_INT >= 26) {
if (systemChromeStyle.systemNavigationBarIconBrightness != null) {
switch (systemChromeStyle.systemNavigationBarIconBrightness) {
case DARK:
// View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
flags |= 0x10;
break;
case LIGHT:
flags &= ~0x10;
break;
}
}
if (systemChromeStyle.systemNavigationBarColor != null) {
window.setNavigationBarColor(systemChromeStyle.systemNavigationBarColor);
// SYSTEM STATUS BAR -------------------------------------------------------------------
// You can't change the color of the system status bar until SDK 21.
// If transparent, SDK 29 and higher may apply a translucent scrim behind the bar to ensure
// proper contrast. This can be overridden with
// SystemChromeStyle.systemStatusBarContrastEnforced.
if (systemChromeStyle.statusBarColor != null
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.setStatusBarColor(systemChromeStyle.statusBarColor);
}
// You can't change the color of the status icons until SDK 23.
if (systemChromeStyle.statusBarIconBrightness != null && Build.VERSION.SDK_INT >= 23) {
switch (systemChromeStyle.statusBarIconBrightness) {
case DARK:
// View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
flags |= 0x2000;
break;
case LIGHT:
flags &= ~0x2000;
break;
}
}
// Build.VERSION_CODES.M
if (Build.VERSION.SDK_INT >= 23) {
if (systemChromeStyle.statusBarIconBrightness != null) {
switch (systemChromeStyle.statusBarIconBrightness) {
case DARK:
// View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
flags |= 0x2000;
break;
case LIGHT:
flags &= ~0x2000;
break;
}
}
if (systemChromeStyle.statusBarColor != null) {
window.setStatusBarColor(systemChromeStyle.statusBarColor);
// You can't override the enforced contrast for a transparent status bar until SDK 29.
// This overrides the translucent scrim that may be placed behind the bar on SDK 29+ to ensure
// contrast is appropriate when using full screen layout modes like Edge to Edge.
if (!systemChromeStyle.systemStatusBarContrastEnforced && Build.VERSION.SDK_INT >= 29) {
window.setStatusBarContrastEnforced(systemChromeStyle.systemStatusBarContrastEnforced);
}
// SYSTEM NAVIGATION BAR --------------------------------------------------------------
// You can't change the color of the system navigation bar until SDK 21.
// If transparent, SDK 29 and higher may apply a translucent scrim behind 2/3 button navigation
// bars to ensure proper contrast. This can be overridden with
// SystemChromeStyle.systemNavigationBarContrastEnforced.
if (systemChromeStyle.systemNavigationBarColor != null
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.setNavigationBarColor(systemChromeStyle.systemNavigationBarColor);
}
// You can't change the color of the navigation buttons until SDK 26.
if (systemChromeStyle.systemNavigationBarIconBrightness != null
&& Build.VERSION.SDK_INT >= 26) {
switch (systemChromeStyle.systemNavigationBarIconBrightness) {
case DARK:
// View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
flags |= 0x10;
break;
case LIGHT:
flags &= ~0x10;
break;
}
}
// You can't change the color of the navigation bar divider color until SDK 28.
if (systemChromeStyle.systemNavigationBarDividerColor != null && Build.VERSION.SDK_INT >= 28) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.setNavigationBarDividerColor(systemChromeStyle.systemNavigationBarDividerColor);
}
// You can't override the enforced contrast for a transparent navigation bar until SDK 29.
// This overrides the translucent scrim that may be placed behind 2/3 button navigation bars on
// SDK 29+ to ensure contrast is appropriate when using full screen layout modes like
// Edge to Edge.
if (!systemChromeStyle.systemNavigationBarContrastEnforced && Build.VERSION.SDK_INT >= 29) {
window.setNavigationBarContrastEnforced(
systemChromeStyle.systemNavigationBarContrastEnforced);
}
view.setSystemUiVisibility(flags);
currentTheme = systemChromeStyle;
}

View File

@ -129,7 +129,8 @@ public class PlatformPluginTest {
when(fakeActivity.getWindow()).thenReturn(fakeWindow);
PlatformChannel fakePlatformChannel = mock(PlatformChannel.class);
PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel);
SystemChromeStyle style = new SystemChromeStyle(0XFF000000, null, 0XFFC70039, null, 0XFF006DB3);
SystemChromeStyle style =
new SystemChromeStyle(0XFF000000, null, true, 0XFFC70039, null, 0XFF006DB3, true);
if (Build.VERSION.SDK_INT >= 28) {
platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style);
@ -140,6 +141,60 @@ public class PlatformPluginTest {
}
}
@Config(sdk = 29)
@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);
if (Build.VERSION.SDK_INT >= 28) {
platformPlugin.mPlatformMessageHandler.showSystemUiMode(
PlatformChannel.SystemUiMode.LEAN_BACK);
assertEquals(
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,
fakeActivity.getWindow().getDecorView().getSystemUiVisibility());
platformPlugin.mPlatformMessageHandler.showSystemUiMode(
PlatformChannel.SystemUiMode.IMMERSIVE);
assertEquals(
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,
fakeActivity.getWindow().getDecorView().getSystemUiVisibility());
platformPlugin.mPlatformMessageHandler.showSystemUiMode(
PlatformChannel.SystemUiMode.IMMERSIVE_STICKY);
assertEquals(
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,
fakeActivity.getWindow().getDecorView().getSystemUiVisibility());
platformPlugin.mPlatformMessageHandler.showSystemUiMode(
PlatformChannel.SystemUiMode.EDGE_TO_EDGE);
assertEquals(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,
fakeActivity.getWindow().getDecorView().getSystemUiVisibility());
}
}
@Test
public void popSystemNavigatorFlutterActivity() {
Activity mockActivity = mock(Activity.class);

View File

@ -74,6 +74,9 @@ using namespace flutter;
} else if ([method isEqualToString:@"SystemChrome.setEnabledSystemUIOverlays"]) {
[self setSystemChromeEnabledSystemUIOverlays:args];
result(nil);
} else if ([method isEqualToString:@"SystemChrome.setEnabledSystemUIMode"]) {
[self setSystemChromeEnabledSystemUIMode:args];
result(nil);
} else if ([method isEqualToString:@"SystemChrome.restoreSystemUIOverlays"]) {
[self restoreSystemChromeSystemUIOverlays];
result(nil);
@ -176,6 +179,26 @@ using namespace flutter;
}
}
- (void)setSystemChromeEnabledSystemUIMode:(NSString*)mode {
// Checks if the top status bar should be visible, reflected by edge to edge setting. This
// platform ignores all other system ui modes.
// We opt out of view controller based status bar visibility since we want
// to be able to modify this on the fly. The key used is
// UIViewControllerBasedStatusBarAppearance
[UIApplication sharedApplication].statusBarHidden =
![mode isEqualToString:@"SystemUiMode.edgeToEdge"];
if ([mode isEqualToString:@"SystemUiMode.edgeToEdge"]) {
[[NSNotificationCenter defaultCenter]
postNotificationName:FlutterViewControllerShowHomeIndicator
object:nil];
} else {
[[NSNotificationCenter defaultCenter]
postNotificationName:FlutterViewControllerHideHomeIndicator
object:nil];
}
}
- (void)restoreSystemChromeSystemUIOverlays {
// Nothing to do on iOS.
}