未验证 提交 5bb144ca 编写于 作者: M Matt Carroll 提交者: GitHub

Re-lands platform brightness support on iOS, plus platform contrast (#10791)

上级 219816de
......@@ -32,6 +32,20 @@ NSNotificationName const FlutterSemanticsUpdateNotification = @"FlutterSemantics
@property(nonatomic, readwrite, getter=isDisplayingFlutterUI) BOOL displayingFlutterUI;
@end
// The following conditional compilation defines an API 13 concept on earlier API targets so that
// a compiler compiling against API 12 or below does not blow up due to non-existent members.
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 130000
typedef enum UIAccessibilityContrast : NSInteger {
UIAccessibilityContrastUnspecified = 0,
UIAccessibilityContrastNormal = 1,
UIAccessibilityContrastHigh = 2
} UIAccessibilityContrast;
@interface UITraitCollection (MethodsFromNewerSDK)
- (UIAccessibilityContrast)accessibilityContrast;
@end
#endif
@implementation FlutterViewController {
std::unique_ptr<fml::WeakPtrFactory<FlutterViewController>> _weakFactory;
fml::scoped_nsobject<FlutterEngine> _engine;
......@@ -434,6 +448,9 @@ NSNotificationName const FlutterSemanticsUpdateNotification = @"FlutterSemantics
_engineNeedsLaunch = NO;
}
// Send platform settings to Flutter, e.g., platform brightness.
[self onUserSettingsChanged:nil];
// Only recreate surface on subsequent appearances when viewport metrics are known.
// First time surface creation is done on viewDidLayoutSubviews.
if (_viewportMetrics.physical_width) {
......@@ -885,10 +902,17 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
#pragma mark - Set user settings
- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
[self onUserSettingsChanged:nil];
}
- (void)onUserSettingsChanged:(NSNotification*)notification {
[[_engine.get() settingsChannel] sendMessage:@{
@"textScaleFactor" : @([self textScaleFactor]),
@"alwaysUse24HourFormat" : @([self isAlwaysUse24HourFormat]),
@"platformBrightness" : [self brightnessMode],
@"platformContrast" : [self contrastMode]
}];
}
......@@ -962,6 +986,40 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
return [dateFormat rangeOfString:@"a"].location == NSNotFound;
}
// The brightness mode of the platform, e.g., light or dark, expressed as a string that
// is understood by the Flutter framework. See the settings system channel for more
// information.
- (NSString*)brightnessMode {
if (@available(iOS 13, *)) {
UIUserInterfaceStyle style = self.traitCollection.userInterfaceStyle;
if (style == UIUserInterfaceStyleDark) {
return @"dark";
} else {
return @"light";
}
} else {
return @"light";
}
}
// The contrast mode of the platform, e.g., normal or high, expressed as a string that is
// understood by the Flutter framework. See the settings system channel for more
// information.
- (NSString*)contrastMode {
if (@available(iOS 13, *)) {
UIAccessibilityContrast contrast = self.traitCollection.accessibilityContrast;
if (contrast == UIAccessibilityContrastHigh) {
return @"high";
} else {
return @"normal";
}
} else {
return @"normal";
}
}
#pragma mark - Status Bar touch event handling
// Standard iOS status bar height in pixels.
......
......@@ -6,9 +6,29 @@
#import <XCTest/XCTest.h>
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
#include "FlutterBinaryMessenger.h"
#if !__has_feature(objc_arc)
#error ARC must be enabled!
#endif
@interface FlutterViewControllerTest : XCTestCase
@end
// The following conditional compilation defines an API 13 concept on earlier API targets so that
// a compiler compiling against API 12 or below does not blow up due to non-existent members.
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 130000
typedef enum UIAccessibilityContrast : NSInteger {
UIAccessibilityContrastUnspecified = 0,
UIAccessibilityContrastNormal = 1,
UIAccessibilityContrastHigh = 2
} UIAccessibilityContrast;
@interface UITraitCollection (MethodsFromNewerSDK)
- (UIAccessibilityContrast)accessibilityContrast;
@end
#endif
@implementation FlutterViewControllerTest
- (void)testBinaryMessenger {
......@@ -23,4 +43,205 @@
OCMVerify([engine binaryMessenger]);
}
#pragma mark - Platform Brightness
- (void)testItReportsLightPlatformBrightnessByDefault {
// Setup test.
id engine = OCMClassMock([FlutterEngine class]);
id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
OCMStub([engine settingsChannel]).andReturn(settingsChannel);
FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
nibName:nil
bundle:nil];
// Exercise behavior under test.
[vc traitCollectionDidChange:nil];
// Verify behavior.
OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
return [message[@"platformBrightness"] isEqualToString:@"light"];
}]]);
// Clean up mocks
[engine stopMocking];
[settingsChannel stopMocking];
}
- (void)testItReportsPlatformBrightnessWhenViewWillAppear {
// Setup test.
id engine = OCMClassMock([FlutterEngine class]);
id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
OCMStub([engine settingsChannel]).andReturn(settingsChannel);
FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
nibName:nil
bundle:nil];
// Exercise behavior under test.
[vc viewWillAppear:false];
// Verify behavior.
OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
return [message[@"platformBrightness"] isEqualToString:@"light"];
}]]);
// Clean up mocks
[engine stopMocking];
[settingsChannel stopMocking];
}
- (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt {
if (!@available(iOS 13, *)) {
return;
}
// Setup test.
id engine = OCMClassMock([FlutterEngine class]);
id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
OCMStub([engine settingsChannel]).andReturn(settingsChannel);
FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine
nibName:nil
bundle:nil];
id mockTraitCollection =
[self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
// We partially mock the real FlutterViewController to act as the OS and report
// the UITraitCollection of our choice. Mocking the object under test is not
// desirable, but given that the OS does not offer a DI approach to providing
// our own UITraitCollection, this seems to be the least bad option.
id partialMockVC = OCMPartialMock(realVC);
OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
// Exercise behavior under test.
[partialMockVC traitCollectionDidChange:nil];
// Verify behavior.
OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
return [message[@"platformBrightness"] isEqualToString:@"dark"];
}]]);
// Clean up mocks
[partialMockVC stopMocking];
[engine stopMocking];
[settingsChannel stopMocking];
[mockTraitCollection stopMocking];
}
// Creates a mocked UITraitCollection with nil values for everything except userInterfaceStyle,
// which is set to the given "style".
- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style {
id mockTraitCollection = OCMClassMock([UITraitCollection class]);
OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style);
return mockTraitCollection;
}
#pragma mark - Platform Contrast
- (void)testItReportsNormalPlatformContrastByDefault {
if (!@available(iOS 13, *)) {
return;
}
// Setup test.
id engine = OCMClassMock([FlutterEngine class]);
id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
OCMStub([engine settingsChannel]).andReturn(settingsChannel);
FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
nibName:nil
bundle:nil];
// Exercise behavior under test.
[vc traitCollectionDidChange:nil];
// Verify behavior.
OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
return [message[@"platformContrast"] isEqualToString:@"normal"];
}]]);
// Clean up mocks
[engine stopMocking];
[settingsChannel stopMocking];
}
- (void)testItReportsPlatformContrastWhenViewWillAppear {
if (!@available(iOS 13, *)) {
return;
}
// Setup test.
id engine = OCMClassMock([FlutterEngine class]);
id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
OCMStub([engine settingsChannel]).andReturn(settingsChannel);
FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
nibName:nil
bundle:nil];
// Exercise behavior under test.
[vc viewWillAppear:false];
// Verify behavior.
OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
return [message[@"platformContrast"] isEqualToString:@"normal"];
}]]);
// Clean up mocks
[engine stopMocking];
[settingsChannel stopMocking];
}
- (void)testItReportsHighContrastWhenTraitCollectionRequestsIt {
if (!@available(iOS 13, *)) {
return;
}
// Setup test.
id engine = OCMClassMock([FlutterEngine class]);
id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
OCMStub([engine settingsChannel]).andReturn(settingsChannel);
FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine
nibName:nil
bundle:nil];
id mockTraitCollection = [self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh];
// We partially mock the real FlutterViewController to act as the OS and report
// the UITraitCollection of our choice. Mocking the object under test is not
// desirable, but given that the OS does not offer a DI approach to providing
// our own UITraitCollection, this seems to be the least bad option.
id partialMockVC = OCMPartialMock(realVC);
OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
// Exercise behavior under test.
[partialMockVC traitCollectionDidChange:mockTraitCollection];
// Verify behavior.
OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
return [message[@"platformContrast"] isEqualToString:@"high"];
}]]);
// Clean up mocks
[partialMockVC stopMocking];
[engine stopMocking];
[settingsChannel stopMocking];
[mockTraitCollection stopMocking];
}
// Creates a mocked UITraitCollection with nil values for everything except accessibilityContrast,
// which is set to the given "contrast".
- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast {
id mockTraitCollection = OCMClassMock([UITraitCollection class]);
OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast);
return mockTraitCollection;
}
@end
......@@ -7,7 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
0D17A5C022D78FCD0057279F /* FlutterViewControllerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.m */; };
0D17A5C022D78FCD0057279F /* FlutterViewControllerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; };
0D4C3FB022DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D4C3FAF22DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; };
0D52D3BD22C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0D52D3B622C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; };
0D6AB6B622BB05E100EEE540 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6AB6B522BB05E100EEE540 /* AppDelegate.m */; };
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册