diff --git a/AUTHORS b/AUTHORS index 12355a9457e43..d3f373b890f91 100644 --- a/AUTHORS +++ b/AUTHORS @@ -15,3 +15,4 @@ Simon Lightfoot Dwayne Slater Tetsuhiro Ueda shoryukenn +SOTEC GmbH & Co. KG \ No newline at end of file diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 87e605e97fd48..907a4a6e3204b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -828,14 +828,36 @@ - (void)onOrientationPreferencesUpdated:(NSNotification*)notification { if (update == nil) { return; } + [self performOrientationUpdate:update.unsignedIntegerValue]; + }); +} - NSUInteger new_preferences = update.unsignedIntegerValue; - - if (new_preferences != _orientationPreferences) { - _orientationPreferences = new_preferences; - [UIViewController attemptRotationToDeviceOrientation]; +- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences { + if (new_preferences != _orientationPreferences) { + _orientationPreferences = new_preferences; + [UIViewController attemptRotationToDeviceOrientation]; + + UIInterfaceOrientationMask currentInterfaceOrientation = + 1 << [[UIApplication sharedApplication] statusBarOrientation]; + if (!(_orientationPreferences & currentInterfaceOrientation)) { + // Force orientation switch if the current orientation is not allowed + if (_orientationPreferences & UIInterfaceOrientationMaskPortrait) { + // This is no official API but more like a workaround / hack (using + // key-value coding on a read-only property). This might break in + // the future, but currently it´s the only way to force an orientation change + [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"]; + } else if (_orientationPreferences & UIInterfaceOrientationMaskPortraitUpsideDown) { + [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortraitUpsideDown) + forKey:@"orientation"]; + } else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeLeft) { + [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) + forKey:@"orientation"]; + } else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeRight) { + [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeRight) + forKey:@"orientation"]; + } } - }); + } } - (BOOL)shouldAutorotate { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m index 22cdeb3d676c0..23a30372d4d37 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m @@ -46,6 +46,10 @@ - (UIAccessibilityContrast)accessibilityContrast; @end #endif +@interface FlutterViewController (Tests) +- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences; +@end + @implementation FlutterViewControllerTest - (void)testBinaryMessenger { @@ -253,6 +257,179 @@ - (void)testItReportsHighContrastWhenTraitCollectionRequestsIt { [mockTraitCollection stopMocking]; } +- (void)testPerformOrientationUpdateForcesOrientationChange { + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortrait]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortrait]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortrait]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeLeft]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeLeft]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeLeft]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeLeft]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeLeft]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeRight]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeRight]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeRight]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortrait]; +} + +- (void)testPerformOrientationUpdateDoesNotForceOrientationChange { + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:NO + resultingOrientation:0]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:NO + resultingOrientation:0]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:NO + resultingOrientation:0]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:NO + resultingOrientation:0]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:NO + resultingOrientation:0]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:NO + resultingOrientation:0]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:NO + resultingOrientation:0]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:NO + resultingOrientation:0]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:NO + resultingOrientation:0]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:NO + resultingOrientation:0]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:NO + resultingOrientation:0]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:NO + resultingOrientation:0]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:NO + resultingOrientation:0]; +} + +// Perform an orientation update test that fails when the expected outcome +// for an orientation update is not met +- (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask + currentOrientation:(UIInterfaceOrientation)currentOrientation + didChangeOrientation:(BOOL)didChange + resultingOrientation:(UIInterfaceOrientation)resultingOrientation { + id engine = OCMClassMock([FlutterEngine class]); + + id deviceMock = OCMPartialMock([UIDevice currentDevice]); + if (!didChange) { + OCMReject([deviceMock setValue:[OCMArg any] forKey:@"orientation"]); + } else { + OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]); + } + + FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine + nibName:nil + bundle:nil]; + id mockApplication = OCMClassMock([UIApplication class]); + OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); + OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation); + + [realVC performOrientationUpdate:mask]; + OCMVerifyAll(deviceMock); + [engine stopMocking]; + [deviceMock stopMocking]; + [mockApplication stopMocking]; +} + // Creates a mocked UITraitCollection with nil values for everything except accessibilityContrast, // which is set to the given "contrast". - (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast {