From d8322207df598760268bdce148662200ce5bac05 Mon Sep 17 00:00:00 2001 From: jesswrd Date: Tue, 14 Jan 2025 16:12:58 -0800 Subject: [PATCH] Fixed XiaoMi statusBar Bug (#161271) Updated usages of statusBar() to systemBar() to fix XiaoMi statusBar bug. Fixes #132831 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --- .../embedding/android/FlutterView.java | 9 +- .../embedding/android/FlutterViewTest.java | 82 ++++++++++++++++++- 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java index a1eaed6da7..608b459e6b 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -689,14 +689,7 @@ public class FlutterView extends FrameLayout (SYSTEM_UI_FLAG_HIDE_NAVIGATION & getWindowSystemUiVisibility()) == 0; if (Build.VERSION.SDK_INT >= API_LEVELS.API_30) { - int mask = 0; - if (navigationBarVisible) { - mask = mask | android.view.WindowInsets.Type.navigationBars(); - } - if (statusBarVisible) { - mask = mask | android.view.WindowInsets.Type.statusBars(); - } - Insets uiInsets = insets.getInsets(mask); + Insets uiInsets = insets.getInsets(android.view.WindowInsets.Type.systemBars()); viewportMetrics.viewPaddingTop = uiInsets.top; viewportMetrics.viewPaddingRight = uiInsets.right; viewportMetrics.viewPaddingBottom = uiInsets.bottom; diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java index 7247873ef1..6cb60a200f 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java @@ -330,6 +330,86 @@ public class FlutterViewTest { validateViewportMetricPadding(viewportMetricsCaptor, 100, 100, 100, 100); } + @SuppressWarnings("deprecation") + // getSystemUiVisibility + // This test uses the API 30+ Algorithm for window insets. This test requires API 34 or + // higher to use the systemOverlays inset. The legacy algorithm is + // set to -1 values, so it is clear if the wrong algorithm is used. + @Test + @TargetApi(34) + @Config(minSdk = 34) + public void reportSystemInsetWhenNotFullscreenForSystemBar() { + // Without custom shadows, the default system ui visibility flags is 0. + FlutterView flutterView = new FlutterView(ctx); + assertEquals(0, flutterView.getSystemUiVisibility()); + + FlutterEngine flutterEngine = spy(new FlutterEngine(ctx, mockFlutterLoader, mockFlutterJni)); + FlutterRenderer flutterRenderer = spy(new FlutterRenderer(mockFlutterJni)); + when(flutterEngine.getRenderer()).thenReturn(flutterRenderer); + + flutterView.attachToFlutterEngine(flutterEngine); + ArgumentCaptor viewportMetricsCaptor = + ArgumentCaptor.forClass(FlutterRenderer.ViewportMetrics.class); + verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture()); + + // When we attach a new FlutterView to the engine without any system insets, the viewport + // metrics default to 0. + assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingTop); + + // Then we simulate the system applying a statusBar inset. + WindowInsets statusBarwindowInsets = + new WindowInsets.Builder() + .setInsets(android.view.WindowInsets.Type.captionBar(), Insets.of(0, 50, 0, 0)) + .setInsets(android.view.WindowInsets.Type.statusBars(), Insets.of(0, 100, 0, 0)) + .build(); + flutterView.onApplyWindowInsets(statusBarwindowInsets); + + // Verify. + verify(flutterRenderer, times(3)).setViewportMetrics(viewportMetricsCaptor.capture()); + // Confirm that the statusBar inset is used because it is the largest of the insets faked. + validateViewportMetricPadding(viewportMetricsCaptor, 0, 100, 0, 0); + clearInvocations(flutterRenderer); + + // Then we simulate the system applying a navigationBar window inset. + WindowInsets navigationBarwindowInsets = + new WindowInsets.Builder() + .setInsets(android.view.WindowInsets.Type.systemOverlays(), Insets.of(0, 0, 0, 10)) + .setInsets(android.view.WindowInsets.Type.navigationBars(), Insets.of(0, 0, 0, 50)) + .build(); + flutterView.onApplyWindowInsets(navigationBarwindowInsets); + + // Verify. + verify(flutterRenderer, times(2)).setViewportMetrics(viewportMetricsCaptor.capture()); + // Confirm that the navigationBar inset is used because it is the largest of the insets faked. + validateViewportMetricPadding(viewportMetricsCaptor, 0, 0, 0, 50); + clearInvocations(flutterRenderer); + + // Then we simulate the system applying a captionBar window inset. + WindowInsets captionBarwindowInsets = + new WindowInsets.Builder() + .setInsets(android.view.WindowInsets.Type.statusBars(), Insets.of(0, 20, 0, 0)) + .setInsets(android.view.WindowInsets.Type.captionBar(), Insets.of(0, 60, 0, 0)) + .build(); + flutterView.onApplyWindowInsets(captionBarwindowInsets); + // Verify. + verify(flutterRenderer, times(2)).setViewportMetrics(viewportMetricsCaptor.capture()); + // Confirm that the captionBar inset is used because it is the largest of the insets faked. + validateViewportMetricPadding(viewportMetricsCaptor, 0, 60, 0, 0); + clearInvocations(flutterRenderer); + + // Then we simulate the system applying a systemOverlay window inset. + WindowInsets systemOverlayWindowInsets = + new WindowInsets.Builder() + .setInsets(android.view.WindowInsets.Type.statusBars(), Insets.of(0, 100, 0, 0)) + .setInsets(android.view.WindowInsets.Type.systemOverlays(), Insets.of(0, 200, 0, 0)) + .build(); + flutterView.onApplyWindowInsets(systemOverlayWindowInsets); + // Verify. + verify(flutterRenderer, times(2)).setViewportMetrics(viewportMetricsCaptor.capture()); + // Confirm that the systemOverlay inset is used because it is the largest of the insets faked. + validateViewportMetricPadding(viewportMetricsCaptor, 0, 200, 0, 0); + } + @SuppressWarnings("deprecation") // getSystemUiVisibility // This test uses the pre-API 30 Algorithm for window insets. @@ -1088,7 +1168,7 @@ public class FlutterViewTest { new WindowInsets.Builder() .setInsets( android.view.WindowInsets.Type.navigationBars() - | android.view.WindowInsets.Type.statusBars(), + | android.view.WindowInsets.Type.systemBars(), Insets.of(100, 100, 100, 100)) .build(); flutterView.onApplyWindowInsets(windowInsets);