From 7d6180c3014a923107a6877ce64b4144cfbf4a47 Mon Sep 17 00:00:00 2001 From: Lazy Llama <10547444+lazylazyllama@users.noreply.github.com> Date: Fri, 30 Oct 2020 01:28:01 +0100 Subject: [PATCH] =?UTF-8?q?Fix=20viewInset.bottom=20and=20viewPadding.bott?= =?UTF-8?q?om=E2=80=A6=20(#21730)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../embedding/android/FlutterView.java | 14 +-- .../android/io/flutter/view/FlutterView.java | 14 +-- .../embedding/android/FlutterViewTest.java | 97 ++++++++++++++++++- 3 files changed, 107 insertions(+), 18 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 8c25da13d9..19fccc60d0 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -580,14 +580,17 @@ public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseC zeroSides = calculateShouldZeroSides(); } - // Status bar (top) and left/right system insets should partially obscure the content - // (padding). + // Status bar (top), navigation bar (bottom) and left/right system insets should + // partially obscure the content (padding). viewportMetrics.paddingTop = statusBarVisible ? insets.getSystemWindowInsetTop() : 0; viewportMetrics.paddingRight = zeroSides == ZeroSides.RIGHT || zeroSides == ZeroSides.BOTH ? 0 : insets.getSystemWindowInsetRight(); - viewportMetrics.paddingBottom = 0; + viewportMetrics.paddingBottom = + navigationBarVisible && guessBottomKeyboardInset(insets) == 0 + ? insets.getSystemWindowInsetBottom() + : 0; viewportMetrics.paddingLeft = zeroSides == ZeroSides.LEFT || zeroSides == ZeroSides.BOTH ? 0 @@ -596,10 +599,7 @@ public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseC // Bottom system inset (keyboard) should adjust scrollable bottom edge (inset). viewportMetrics.viewInsetTop = 0; viewportMetrics.viewInsetRight = 0; - viewportMetrics.viewInsetBottom = - navigationBarVisible - ? insets.getSystemWindowInsetBottom() - : guessBottomKeyboardInset(insets); + viewportMetrics.viewInsetBottom = guessBottomKeyboardInset(insets); viewportMetrics.viewInsetLeft = 0; } diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index b630ad242a..9f0e85e91e 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -662,14 +662,17 @@ public class FlutterView extends SurfaceView zeroSides = calculateShouldZeroSides(); } - // Status bar (top) and left/right system insets should partially obscure the content - // (padding). + // Status bar (top), navigation bar (bottom) and left/right system insets should + // partially obscure the content (padding). mMetrics.physicalPaddingTop = statusBarVisible ? insets.getSystemWindowInsetTop() : 0; mMetrics.physicalPaddingRight = zeroSides == ZeroSides.RIGHT || zeroSides == ZeroSides.BOTH ? 0 : insets.getSystemWindowInsetRight(); - mMetrics.physicalPaddingBottom = 0; + mMetrics.physicalPaddingBottom = + navigationBarVisible && guessBottomKeyboardInset(insets) == 0 + ? insets.getSystemWindowInsetBottom() + : 0; mMetrics.physicalPaddingLeft = zeroSides == ZeroSides.LEFT || zeroSides == ZeroSides.BOTH ? 0 @@ -678,10 +681,7 @@ public class FlutterView extends SurfaceView // Bottom system inset (keyboard) should adjust scrollable bottom edge (inset). mMetrics.physicalViewInsetTop = 0; mMetrics.physicalViewInsetRight = 0; - mMetrics.physicalViewInsetBottom = - navigationBarVisible - ? insets.getSystemWindowInsetBottom() - : guessBottomKeyboardInset(insets); + mMetrics.physicalViewInsetBottom = guessBottomKeyboardInset(insets); mMetrics.physicalViewInsetLeft = 0; } diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java index 204799d361..d503e62158 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java @@ -210,8 +210,12 @@ public class FlutterViewTest { assertEquals(SettingsChannel.PlatformBrightness.dark, reportedBrightness.get()); } + // This test uses the API 30+ Algorithm for window insets. The legacy algorithm is + // set to -1 values, so it is clear if the wrong algorithm is used. @Test + @TargetApi(30) @Config( + sdk = 30, shadows = { FlutterViewTest.ShadowFullscreenView.class, FlutterViewTest.ShadowFullscreenViewGroup.class @@ -249,7 +253,52 @@ public class FlutterViewTest { assertEquals(100, viewportMetricsCaptor.getValue().paddingRight); } + // This test uses the pre-API 30 Algorithm for window insets. + @Test + @TargetApi(29) + @Config( + sdk = 29, + shadows = { + FlutterViewTest.ShadowFullscreenView.class, + FlutterViewTest.ShadowFullscreenViewGroup.class + }) + public void setPaddingTopToZeroForFullscreenModeLegacy() { + 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 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); + assertEquals(100, viewportMetricsCaptor.getValue().paddingBottom); + assertEquals(100, viewportMetricsCaptor.getValue().paddingLeft); + assertEquals(100, viewportMetricsCaptor.getValue().paddingRight); + } + + // This test uses the API 30+ Algorithm for window insets. The legacy algorithm is + // set to -1 values, so it is clear if the wrong algorithm is used. @Test + @TargetApi(30) + @Config(sdk = 30) public void reportSystemInsetWhenNotFullscreen() { // Without custom shadows, the default system ui visibility flags is 0. FlutterView flutterView = new FlutterView(RuntimeEnvironment.application); @@ -287,6 +336,46 @@ public class FlutterViewTest { assertEquals(100, viewportMetricsCaptor.getValue().paddingRight); } + // This test uses the pre-API 30 Algorithm for window insets. + @Test + @TargetApi(29) + @Config(sdk = 29) + public void reportSystemInsetWhenNotFullscreenLegacy() { + // 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 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); + assertEquals(0, viewportMetricsCaptor.getValue().paddingBottom); + assertEquals(100, viewportMetricsCaptor.getValue().paddingLeft); + assertEquals(100, viewportMetricsCaptor.getValue().paddingRight); + } + @Test public void systemInsetHandlesFullscreenNavbarRight() { RuntimeEnvironment.setQualifiers("+land"); @@ -327,10 +416,10 @@ public class FlutterViewTest { verify(flutterRenderer, times(2)).setViewportMetrics(viewportMetricsCaptor.capture()); // Top padding is removed due to full screen. assertEquals(0, viewportMetricsCaptor.getValue().paddingTop); - // Padding bottom is always 0. + // Bottom padding is removed due to hide navigation. assertEquals(0, viewportMetricsCaptor.getValue().paddingBottom); assertEquals(100, viewportMetricsCaptor.getValue().paddingLeft); - // Right padding is zero because the rotation is 270deg + // Right padding is zero because the rotation is 90deg assertEquals(0, viewportMetricsCaptor.getValue().paddingRight); } @@ -374,7 +463,7 @@ public class FlutterViewTest { verify(flutterRenderer, times(2)).setViewportMetrics(viewportMetricsCaptor.capture()); // Top padding is removed due to full screen. assertEquals(0, viewportMetricsCaptor.getValue().paddingTop); - // Padding bottom is always 0. + // Bottom padding is removed due to hide navigation. assertEquals(0, viewportMetricsCaptor.getValue().paddingBottom); // Left padding is zero because the rotation is 270deg assertEquals(0, viewportMetricsCaptor.getValue().paddingLeft); @@ -477,7 +566,7 @@ public class FlutterViewTest { verify(flutterRenderer, times(2)).setViewportMetrics(viewportMetricsCaptor.capture()); // Top padding is removed due to full screen. assertEquals(0, viewportMetricsCaptor.getValue().paddingTop); - // Padding bottom is always 0. + // Bottom padding is removed due to hide navigation. assertEquals(0, viewportMetricsCaptor.getValue().paddingBottom); // Left padding is zero because the rotation is 270deg assertEquals(0, viewportMetricsCaptor.getValue().paddingLeft); -- GitLab