Partially remove setSystemUiVisibility() usages (flutter/engine#31092)

This commit is contained in:
Camille Simon
2022-02-17 12:54:02 -08:00
committed by GitHub
parent d1d733c608
commit fc7b6647b5
5 changed files with 338 additions and 90 deletions

View File

@@ -24,9 +24,9 @@ import android.view.View;
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;
import io.flutter.util.Preconditions;
import io.flutter.view.FlutterMain;
import io.flutter.view.FlutterNativeView;
@@ -141,7 +141,14 @@ public final class FlutterActivityDelegate
Window window = activity.getWindow();
window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(0x40000000);
window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI);
WindowCompat.setDecorFitsSystemWindows(window, false);
if (Build.VERSION.SDK_INT < 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(
view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
}
}
String[] args = getArgsFromIntent(activity.getIntent());

View File

@@ -38,6 +38,7 @@ import androidx.annotation.NonNull;
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;
@@ -590,7 +591,14 @@ public class FlutterActivity extends Activity
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(0x40000000);
window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI);
WindowCompat.setDecorFitsSystemWindows(window, false);
if (Build.VERSION.SDK_INT < 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(
view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
}
}
}

View File

@@ -38,6 +38,7 @@ import androidx.annotation.NonNull;
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;
@@ -45,7 +46,6 @@ import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode;
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;
/**
@@ -495,7 +495,14 @@ public class FlutterFragmentActivity extends FragmentActivity
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(0x40000000);
window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI);
WindowCompat.setDecorFitsSystemWindows(window, false);
if (Build.VERSION.SDK_INT < 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(
view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
}
}
}

View File

@@ -20,6 +20,8 @@ import androidx.activity.OnBackPressedDispatcherOwner;
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,13 @@ import java.util.List;
/** Android implementation of the platform plugin. */
public class PlatformPlugin {
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;
private final PlatformPluginDelegate platformPluginDelegate;
private PlatformChannel.SystemChromeStyle currentTheme;
private int mEnabledOverlays;
private PlatformChannel.SystemUiMode currentSystemUiMode;
private List<PlatformChannel.SystemUiOverlay> currentOverlays;
private static final String TAG = "PlatformPlugin";
/**
@@ -140,8 +141,6 @@ public class PlatformPlugin {
this.platformChannel = platformChannel;
this.platformChannel.setPlatformMessageHandler(mPlatformMessageHandler);
this.platformPluginDelegate = delegate;
mEnabledOverlays = DEFAULT_SYSTEM_UI;
}
/**
@@ -238,17 +237,75 @@ public class PlatformPlugin {
}
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.
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 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
// 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 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
// 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 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.
// 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 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.
// 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
@@ -258,12 +315,8 @@ public class PlatformPlugin {
} 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.
// 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
@@ -274,11 +327,8 @@ public class PlatformPlugin {
} 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.
// 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
@@ -286,44 +336,69 @@ public class PlatformPlugin {
| 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(
List<PlatformChannel.SystemUiOverlay> overlaysToShow) {
// Start by assuming we want to hide all system overlays (like an immersive
// game).
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.
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;
}
private void setSystemChromeEnabledSystemUIOverlaysLegacy(
List<PlatformChannel.SystemUiOverlay> overlaysToShow) {
int enabledOverlays =
DEFAULT_SYSTEM_UI
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;
// 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) {
currentSystemUiMode = PlatformChannel.SystemUiMode.IMMERSIVE_STICKY;
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) {
@@ -336,9 +411,7 @@ public class PlatformPlugin {
break;
}
}
mEnabledOverlays = enabledOverlays;
updateSystemUiOverlays();
activity.getWindow().getDecorView().setSystemUiVisibility(enabledOverlays);
}
/**
@@ -350,9 +423,24 @@ public class PlatformPlugin {
* PlatformPlugin}.
*/
public void updateSystemUiOverlays() {
activity.getWindow().getDecorView().setSystemUiVisibility(mEnabledOverlays);
if (currentTheme != null) {
setSystemChromeSystemUIOverlayStyle(currentTheme);
if (currentOverlays != null) {
setSystemChromeEnabledSystemUIOverlays(currentOverlays);
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 < 30,
// as dictated by the implementation of WindowCompat.
View view = window.getDecorView();
view.setSystemUiVisibility(
view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
}
}
}
}
}

View File

@@ -13,6 +13,7 @@ import static org.mockito.Mockito.times;
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.View;
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 androidx.test.ext.junit.runners.AndroidJUnit4;
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
@@ -32,6 +35,8 @@ import io.flutter.embedding.engine.systemchannels.PlatformChannel.ClipboardConte
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,9 @@ public class PlatformPluginTest {
@Config(sdk = 29)
@Test
public void setSystemUiMode() {
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);
@@ -325,46 +332,177 @@ public class PlatformPluginTest {
if (Build.VERSION.SDK_INT >= 28) {
platformPlugin.mPlatformMessageHandler.showSystemUiMode(
PlatformChannel.SystemUiMode.LEAN_BACK);
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);
platformPlugin.mPlatformMessageHandler.showSystemUiMode(
PlatformChannel.SystemUiMode.IMMERSIVE);
verify(fakeDecorView)
.setSystemUiVisibility(
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);
platformPlugin.mPlatformMessageHandler.showSystemUiMode(
PlatformChannel.SystemUiMode.IMMERSIVE_STICKY);
verify(fakeDecorView)
.setSystemUiVisibility(
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);
}
if (Build.VERSION.SDK_INT >= 29) {
platformPlugin.mPlatformMessageHandler.showSystemUiMode(
PlatformChannel.SystemUiMode.EDGE_TO_EDGE);
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);
platformPlugin.mPlatformMessageHandler.showSystemUiMode(
PlatformChannel.SystemUiMode.IMMERSIVE);
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_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
platformPlugin.mPlatformMessageHandler.showSystemUiMode(
PlatformChannel.SystemUiMode.IMMERSIVE_STICKY);
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_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
if (Build.VERSION.SDK_INT >= 29) {
platformPlugin.mPlatformMessageHandler.showSystemUiMode(
PlatformChannel.SystemUiMode.EDGE_TO_EDGE);
verify(fakeDecorView, times(4))
.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
}
@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() {
// 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);
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<PlatformChannel.SystemUiOverlay>());
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<PlatformChannel.SystemUiOverlay>(
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<PlatformChannel.SystemUiOverlay>());
verify(fakeWindowInsetsController)
.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
verify(fakeWindowInsetsController).hide(WindowInsetsCompat.Type.systemBars());
platformPlugin.mPlatformMessageHandler.showSystemOverlays(
new ArrayList<PlatformChannel.SystemUiOverlay>(
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)