mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Engine] Support for Android Fullscreen Modes (flutter/engine#25785)
This commit is contained in:
parent
2a6abe950b
commit
ea1ff8ab3b
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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.
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user