From bec0dacaae419e798056f2eebdf9b69e80c252df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E6=B4=B5=E9=94=8B?= Date: Wed, 22 Nov 2023 18:49:13 -0600 Subject: [PATCH] Fix not being able to hide iOS status bar via setEnabledSystemUIMode (#48271) Fix https://github.com/flutter/flutter/issues/138604 and https://github.com/flutter/flutter/issues/138671 Related PR https://github.com/flutter/engine/pull/45351 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style --- .../framework/Source/FlutterPlatformPlugin.mm | 4 +- .../Source/FlutterPlatformPluginTest.mm | 89 +++++++++++++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm index 4739c3119fc23..281ab64092ea8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm @@ -43,7 +43,7 @@ using namespace flutter; static void SetStatusBarHiddenForSharedApplication(BOOL hidden) { -#if APPLICATION_EXTENSION_API_ONLY +#if not APPLICATION_EXTENSION_API_ONLY [UIApplication sharedApplication].statusBarHidden = hidden; #else FML_LOG(WARNING) << "Application based status bar styling is not available in app extension."; @@ -51,7 +51,7 @@ static void SetStatusBarHiddenForSharedApplication(BOOL hidden) { } static void SetStatusBarStyleForSharedApplication(UIStatusBarStyle style) { -#if APPLICATION_EXTENSION_API_ONLY +#if not APPLICATION_EXTENSION_API_ONLY // Note: -[UIApplication setStatusBarStyle] is deprecated in iOS9 // in favor of delegating to the view controller. [[UIApplication sharedApplication] setStatusBarStyle:style]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm index 25907899edbdb..38096e9cbf320 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm @@ -408,4 +408,93 @@ - (void)testViewControllerBasedStatusBarHiddenUpdate { [bundleMock stopMocking]; } +- (void)testStatusBarHiddenUpdate { + id bundleMock = OCMPartialMock([NSBundle mainBundle]); + OCMStub([bundleMock objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"]) + .andReturn(@NO); + id mockApplication = OCMClassMock([UIApplication class]); + OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); + + // Enabling system UI overlays to update status bar. + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil]; + [engine runWithEntrypoint:nil]; + FlutterViewController* flutterViewController = + [[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil]; + std::unique_ptr> _weakFactory = + std::make_unique>(engine); + + // Update to hidden. + FlutterPlatformPlugin* plugin = [engine platformPlugin]; + + XCTestExpectation* enableSystemUIOverlaysCalled = + [self expectationWithDescription:@"setEnabledSystemUIOverlays"]; + FlutterResult resultSet = ^(id result) { + [enableSystemUIOverlaysCalled fulfill]; + }; + FlutterMethodCall* methodCallSet = + [FlutterMethodCall methodCallWithMethodName:@"SystemChrome.setEnabledSystemUIOverlays" + arguments:@[ @"SystemUiOverlay.bottom" ]]; + [plugin handleMethodCall:methodCallSet result:resultSet]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +#if not APPLICATION_EXTENSION_API_ONLY + OCMVerify([mockApplication setStatusBarHidden:YES]); +#endif + + // Update to shown. + XCTestExpectation* enableSystemUIOverlaysCalled2 = + [self expectationWithDescription:@"setEnabledSystemUIOverlays"]; + FlutterResult resultSet2 = ^(id result) { + [enableSystemUIOverlaysCalled2 fulfill]; + }; + FlutterMethodCall* methodCallSet2 = + [FlutterMethodCall methodCallWithMethodName:@"SystemChrome.setEnabledSystemUIOverlays" + arguments:@[ @"SystemUiOverlay.top" ]]; + [plugin handleMethodCall:methodCallSet2 result:resultSet2]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +#if not APPLICATION_EXTENSION_API_ONLY + OCMVerify([mockApplication setStatusBarHidden:NO]); +#endif + + [flutterViewController deregisterNotifications]; + [mockApplication stopMocking]; + [bundleMock stopMocking]; +} + +- (void)testStatusBarStyle { + id bundleMock = OCMPartialMock([NSBundle mainBundle]); + OCMStub([bundleMock objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"]) + .andReturn(@NO); + id mockApplication = OCMClassMock([UIApplication class]); + OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); + + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil]; + [engine runWithEntrypoint:nil]; + FlutterViewController* flutterViewController = + [[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil]; + std::unique_ptr> _weakFactory = + std::make_unique>(engine); + XCTAssertFalse(flutterViewController.prefersStatusBarHidden); + + FlutterPlatformPlugin* plugin = [engine platformPlugin]; + + XCTestExpectation* enableSystemUIModeCalled = + [self expectationWithDescription:@"setSystemUIOverlayStyle"]; + FlutterResult resultSet = ^(id result) { + [enableSystemUIModeCalled fulfill]; + }; + FlutterMethodCall* methodCallSet = + [FlutterMethodCall methodCallWithMethodName:@"SystemChrome.setSystemUIOverlayStyle" + arguments:@{@"statusBarBrightness" : @"Brightness.dark"}]; + [plugin handleMethodCall:methodCallSet result:resultSet]; + [self waitForExpectationsWithTimeout:1 handler:nil]; + +#if not APPLICATION_EXTENSION_API_ONLY + OCMVerify([mockApplication setStatusBarStyle:UIStatusBarStyleLightContent]); +#endif + + [flutterViewController deregisterNotifications]; + [mockApplication stopMocking]; + [bundleMock stopMocking]; +} + @end