remove top padding when system UI in fullscreen mode (#17985)

This commit is contained in:
xster 2020-04-28 12:21:37 -07:00 committed by GitHub
parent 893cb3731c
commit 45e6cfd1ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 108 additions and 2 deletions

View File

@ -400,10 +400,11 @@ public class FlutterView extends FrameLayout {
@SuppressLint({"InlinedApi", "NewApi"})
@NonNull
public final WindowInsets onApplyWindowInsets(@NonNull WindowInsets insets) {
boolean statusBarHidden = (SYSTEM_UI_FLAG_FULLSCREEN & getWindowSystemUiVisibility()) != 0;
WindowInsets newInsets = super.onApplyWindowInsets(insets);
// Status bar (top) and left/right system insets should partially obscure the content (padding).
viewportMetrics.paddingTop = insets.getSystemWindowInsetTop();
viewportMetrics.paddingTop = statusBarHidden ? 0 : insets.getSystemWindowInsetTop();
viewportMetrics.paddingRight = insets.getSystemWindowInsetRight();
viewportMetrics.paddingBottom = 0;
viewportMetrics.paddingLeft = insets.getSystemWindowInsetLeft();

View File

@ -8,9 +8,13 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.loader.FlutterLoader;
@ -21,6 +25,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
@ -29,9 +34,16 @@ import org.mockito.stubbing.Answer;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Config(manifest = Config.NONE)
// TODO(xster): we have 2 versions of robolectric Android shadows in
// shell/platform/android/embedding_bundle/build.gradle. Remove the older
// org.robolectric:android-all:4.1.2_r1-robolectric-r1 which doesn't have the right shadow
// behaviors.
@Config(manifest = Config.NONE, sdk = 27)
@RunWith(RobolectricTestRunner.class)
@TargetApi(27)
public class FlutterViewTest {
@Mock FlutterJNI mockFlutterJni;
@Mock FlutterLoader mockFlutterLoader;
@ -188,4 +200,97 @@ public class FlutterViewTest {
// Verify results.
assertEquals(SettingsChannel.PlatformBrightness.dark, reportedBrightness.get());
}
@Test
@Config(
shadows = {
FlutterViewTest.ShadowFullscreenView.class,
FlutterViewTest.ShadowFullscreenViewGroup.class
})
public void setPaddingTopToZeroForFullscreenMode() {
FlutterView flutterView = new FlutterView(RuntimeEnvironment.application);
FlutterEngine flutterEngine =
spy(new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni));
FlutterRenderer flutterRenderer = spy(new FlutterRenderer(mockFlutterJni));
when(flutterEngine.getRenderer()).thenReturn(flutterRenderer);
// When we attach a new FlutterView to the engine without any system insets, the viewport
// metrics
// default to 0.
flutterView.attachToFlutterEngine(flutterEngine);
ArgumentCaptor<FlutterRenderer.ViewportMetrics> viewportMetricsCaptor =
ArgumentCaptor.forClass(FlutterRenderer.ViewportMetrics.class);
verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture());
assertEquals(0, viewportMetricsCaptor.getValue().paddingTop);
// Then we simulate the system applying a window inset.
WindowInsets windowInsets = mock(WindowInsets.class);
when(windowInsets.getSystemWindowInsetTop()).thenReturn(100);
when(windowInsets.getSystemWindowInsetBottom()).thenReturn(100);
when(windowInsets.getSystemWindowInsetLeft()).thenReturn(100);
when(windowInsets.getSystemWindowInsetRight()).thenReturn(100);
flutterView.onApplyWindowInsets(windowInsets);
// Verify.
verify(flutterRenderer, times(2)).setViewportMetrics(viewportMetricsCaptor.capture());
assertEquals(0, viewportMetricsCaptor.getValue().paddingTop);
// Padding bottom is always 0.
assertEquals(0, viewportMetricsCaptor.getValue().paddingBottom);
assertEquals(100, viewportMetricsCaptor.getValue().paddingLeft);
assertEquals(100, viewportMetricsCaptor.getValue().paddingRight);
}
@Test
public void reportSystemInsetWhenNotFullscreen() {
// Without custom shadows, the default system ui visibility flags is 0.
FlutterView flutterView = new FlutterView(RuntimeEnvironment.application);
assertEquals(0, flutterView.getSystemUiVisibility());
FlutterEngine flutterEngine =
spy(new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni));
FlutterRenderer flutterRenderer = spy(new FlutterRenderer(mockFlutterJni));
when(flutterEngine.getRenderer()).thenReturn(flutterRenderer);
// When we attach a new FlutterView to the engine without any system insets, the viewport
// metrics
// default to 0.
flutterView.attachToFlutterEngine(flutterEngine);
ArgumentCaptor<FlutterRenderer.ViewportMetrics> viewportMetricsCaptor =
ArgumentCaptor.forClass(FlutterRenderer.ViewportMetrics.class);
verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture());
assertEquals(0, viewportMetricsCaptor.getValue().paddingTop);
// Then we simulate the system applying a window inset.
WindowInsets windowInsets = mock(WindowInsets.class);
when(windowInsets.getSystemWindowInsetTop()).thenReturn(100);
when(windowInsets.getSystemWindowInsetBottom()).thenReturn(100);
when(windowInsets.getSystemWindowInsetLeft()).thenReturn(100);
when(windowInsets.getSystemWindowInsetRight()).thenReturn(100);
flutterView.onApplyWindowInsets(windowInsets);
// Verify.
verify(flutterRenderer, times(2)).setViewportMetrics(viewportMetricsCaptor.capture());
// Top padding is reported as-is.
assertEquals(100, viewportMetricsCaptor.getValue().paddingTop);
// Padding bottom is always 0.
assertEquals(0, viewportMetricsCaptor.getValue().paddingBottom);
assertEquals(100, viewportMetricsCaptor.getValue().paddingLeft);
assertEquals(100, viewportMetricsCaptor.getValue().paddingRight);
}
/*
* A custom shadow that reports fullscreen flag for system UI visibility
*/
@Implements(View.class)
public static class ShadowFullscreenView {
@Implementation
public int getWindowSystemUiVisibility() {
return View.SYSTEM_UI_FLAG_FULLSCREEN;
}
}
// ViewGroup is the first shadow in the type hierarchy for FlutterView. Shadows need to mimic
// production classes' view hierarchy.
@Implements(ViewGroup.class)
public static class ShadowFullscreenViewGroup extends ShadowFullscreenView {}
}