Properly calculate alwaysUse24HourFormat on MacOS (flutter/engine#53795)
Moves the implementation if isAlwaysUse24HourFormat from iOS's FlutterViewController internals to common utility, and makes use of it on MacOS in order to return correct value of `alwaysUse24HourFormat`. This PR partially resolves [#32006](https://github.com/flutter/flutter/issues/32006). Note that on iOS 16+ and MacOS 13+, there is a new API for obtaining this information: https://developer.apple.com/documentation/foundation/locale/components/3952289-hourcycle. However, to keep things simpler, I wanted to not include changes to the logic. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
parent
88b005388d
commit
85c39faa9b
@ -43580,6 +43580,7 @@ ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterB
|
|||||||
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterDartProject.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterDartProject.h + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterHourFormat.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterTexture.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterTexture.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterBinaryMessengerRelay.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterBinaryMessengerRelay.h + ../../../flutter/LICENSE
|
||||||
@ -43588,6 +43589,7 @@ ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterBi
|
|||||||
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterChannels.mm + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterChannels.mm + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterHourFormat.mm + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.mm + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.mm + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm + ../../../flutter/LICENSE
|
||||||
@ -46480,6 +46482,7 @@ FILE: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterBin
|
|||||||
FILE: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h
|
||||||
FILE: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h
|
||||||
FILE: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterDartProject.h
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterDartProject.h
|
||||||
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterHourFormat.h
|
||||||
FILE: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h
|
||||||
FILE: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterTexture.h
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Headers/FlutterTexture.h
|
||||||
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterBinaryMessengerRelay.h
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterBinaryMessengerRelay.h
|
||||||
@ -46488,6 +46491,7 @@ FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterBina
|
|||||||
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterChannels.mm
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterChannels.mm
|
||||||
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m
|
||||||
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm
|
||||||
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterHourFormat.mm
|
||||||
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.h
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.h
|
||||||
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.mm
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.mm
|
||||||
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm
|
FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm
|
||||||
|
@ -85,6 +85,7 @@ source_set("framework_common") {
|
|||||||
"framework/Source/FlutterBinaryMessengerRelay.mm",
|
"framework/Source/FlutterBinaryMessengerRelay.mm",
|
||||||
"framework/Source/FlutterChannels.mm",
|
"framework/Source/FlutterChannels.mm",
|
||||||
"framework/Source/FlutterCodecs.mm",
|
"framework/Source/FlutterCodecs.mm",
|
||||||
|
"framework/Source/FlutterHourFormat.mm",
|
||||||
"framework/Source/FlutterNSBundleUtils.h",
|
"framework/Source/FlutterNSBundleUtils.h",
|
||||||
"framework/Source/FlutterNSBundleUtils.mm",
|
"framework/Source/FlutterNSBundleUtils.mm",
|
||||||
"framework/Source/FlutterStandardCodec.mm",
|
"framework/Source/FlutterStandardCodec.mm",
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_FRAMEWORK_HEADERS_FLUTTERHOURFORMAT_H_
|
||||||
|
#define FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_FRAMEWORK_HEADERS_FLUTTERHOURFORMAT_H_
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@interface FlutterHourFormat : NSObject
|
||||||
|
+ (BOOL)isAlwaysUse24HourFormat;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_FRAMEWORK_HEADERS_FLUTTERHOURFORMAT_H_
|
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterHourFormat.h"
|
||||||
|
|
||||||
|
@implementation FlutterHourFormat
|
||||||
|
+ (BOOL)isAlwaysUse24HourFormat {
|
||||||
|
// iOS does not report its "24-Hour Time" user setting in the API. Instead, it applies
|
||||||
|
// it automatically to NSDateFormatter when used with [NSLocale currentLocale]. It is
|
||||||
|
// essential that [NSLocale currentLocale] is used. Any custom locale, even the one
|
||||||
|
// that's the same as [NSLocale currentLocale] will ignore the 24-hour option (there
|
||||||
|
// must be some internal field that's not exposed to developers).
|
||||||
|
//
|
||||||
|
// Therefore this option behaves differently across Android and iOS. On Android this
|
||||||
|
// setting is exposed standalone, and can therefore be applied to all locales, whether
|
||||||
|
// the "current system locale" or a custom one. On iOS it only applies to the current
|
||||||
|
// system locale. Widget implementors must take this into account in order to provide
|
||||||
|
// platform-idiomatic behavior in their widgets.
|
||||||
|
NSString* dateFormat = [NSDateFormatter dateFormatFromTemplate:@"j"
|
||||||
|
options:0
|
||||||
|
locale:[NSLocale currentLocale]];
|
||||||
|
return [dateFormat rangeOfString:@"a"].location == NSNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -8,6 +8,7 @@ framework_common_headers =
|
|||||||
"framework/Headers/FlutterBinaryMessenger.h",
|
"framework/Headers/FlutterBinaryMessenger.h",
|
||||||
"framework/Headers/FlutterChannels.h",
|
"framework/Headers/FlutterChannels.h",
|
||||||
"framework/Headers/FlutterCodecs.h",
|
"framework/Headers/FlutterCodecs.h",
|
||||||
|
"framework/Headers/FlutterHourFormat.h",
|
||||||
"framework/Headers/FlutterTexture.h",
|
"framework/Headers/FlutterTexture.h",
|
||||||
"framework/Headers/FlutterDartProject.h",
|
"framework/Headers/FlutterDartProject.h",
|
||||||
],
|
],
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#import "FlutterBinaryMessenger.h"
|
#import "FlutterBinaryMessenger.h"
|
||||||
#import "FlutterDartProject.h"
|
#import "FlutterDartProject.h"
|
||||||
#import "FlutterEngine.h"
|
#import "FlutterEngine.h"
|
||||||
|
#import "FlutterHourFormat.h"
|
||||||
#import "FlutterMacros.h"
|
#import "FlutterMacros.h"
|
||||||
#import "FlutterPlugin.h"
|
#import "FlutterPlugin.h"
|
||||||
#import "FlutterTexture.h"
|
#import "FlutterTexture.h"
|
||||||
|
@ -2158,7 +2158,7 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
|||||||
- (void)onUserSettingsChanged:(NSNotification*)notification {
|
- (void)onUserSettingsChanged:(NSNotification*)notification {
|
||||||
[[_engine.get() settingsChannel] sendMessage:@{
|
[[_engine.get() settingsChannel] sendMessage:@{
|
||||||
@"textScaleFactor" : @([self textScaleFactor]),
|
@"textScaleFactor" : @([self textScaleFactor]),
|
||||||
@"alwaysUse24HourFormat" : @([self isAlwaysUse24HourFormat]),
|
@"alwaysUse24HourFormat" : @([FlutterHourFormat isAlwaysUse24HourFormat]),
|
||||||
@"platformBrightness" : [self brightnessMode],
|
@"platformBrightness" : [self brightnessMode],
|
||||||
@"platformContrast" : [self contrastMode],
|
@"platformContrast" : [self contrastMode],
|
||||||
@"nativeSpellCheckServiceDefined" : @true,
|
@"nativeSpellCheckServiceDefined" : @true,
|
||||||
@ -2232,24 +2232,6 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isAlwaysUse24HourFormat {
|
|
||||||
// iOS does not report its "24-Hour Time" user setting in the API. Instead, it applies
|
|
||||||
// it automatically to NSDateFormatter when used with [NSLocale currentLocale]. It is
|
|
||||||
// essential that [NSLocale currentLocale] is used. Any custom locale, even the one
|
|
||||||
// that's the same as [NSLocale currentLocale] will ignore the 24-hour option (there
|
|
||||||
// must be some internal field that's not exposed to developers).
|
|
||||||
//
|
|
||||||
// Therefore this option behaves differently across Android and iOS. On Android this
|
|
||||||
// setting is exposed standalone, and can therefore be applied to all locales, whether
|
|
||||||
// the "current system locale" or a custom one. On iOS it only applies to the current
|
|
||||||
// system locale. Widget implementors must take this into account in order to provide
|
|
||||||
// platform-idiomatic behavior in their widgets.
|
|
||||||
NSString* dateFormat = [NSDateFormatter dateFormatFromTemplate:@"j"
|
|
||||||
options:0
|
|
||||||
locale:[NSLocale currentLocale]];
|
|
||||||
return [dateFormat rangeOfString:@"a"].location == NSNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The brightness mode of the platform, e.g., light or dark, expressed as a string that
|
// 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
|
// is understood by the Flutter framework. See the settings
|
||||||
// system channel for more information.
|
// system channel for more information.
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "flutter/lib/ui/window/pointer_data.h"
|
#include "flutter/lib/ui/window/pointer_data.h"
|
||||||
#import "flutter/lib/ui/window/viewport_metrics.h"
|
#import "flutter/lib/ui/window/viewport_metrics.h"
|
||||||
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h"
|
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h"
|
||||||
|
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterHourFormat.h"
|
||||||
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
|
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
|
||||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
|
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
|
||||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEmbedderKeyResponder.h"
|
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEmbedderKeyResponder.h"
|
||||||
@ -1335,6 +1336,35 @@ extern NSNotificationName const FlutterViewControllerWillDealloc;
|
|||||||
[mockTraitCollection stopMocking];
|
[mockTraitCollection stopMocking];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)testItReportsAlwaysUsed24HourFormat {
|
||||||
|
// Setup test.
|
||||||
|
id settingsChannel = OCMStrictClassMock([FlutterBasicMessageChannel class]);
|
||||||
|
OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel);
|
||||||
|
FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine
|
||||||
|
nibName:nil
|
||||||
|
bundle:nil];
|
||||||
|
// Test the YES case.
|
||||||
|
id mockHourFormat = OCMClassMock([FlutterHourFormat class]);
|
||||||
|
OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(YES);
|
||||||
|
OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
|
||||||
|
return [message[@"alwaysUse24HourFormat"] isEqual:@(YES)];
|
||||||
|
}]]);
|
||||||
|
[vc onUserSettingsChanged:nil];
|
||||||
|
[mockHourFormat stopMocking];
|
||||||
|
|
||||||
|
// Test the NO case.
|
||||||
|
mockHourFormat = OCMClassMock([FlutterHourFormat class]);
|
||||||
|
OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(NO);
|
||||||
|
OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
|
||||||
|
return [message[@"alwaysUse24HourFormat"] isEqual:@(NO)];
|
||||||
|
}]]);
|
||||||
|
[vc onUserSettingsChanged:nil];
|
||||||
|
[mockHourFormat stopMocking];
|
||||||
|
|
||||||
|
// Clean up mocks.
|
||||||
|
[settingsChannel stopMocking];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagNotSet {
|
- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagNotSet {
|
||||||
if (@available(iOS 13, *)) {
|
if (@available(iOS 13, *)) {
|
||||||
// noop
|
// noop
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#import "FlutterAppLifecycleDelegate.h"
|
#import "FlutterAppLifecycleDelegate.h"
|
||||||
#import "FlutterBinaryMessenger.h"
|
#import "FlutterBinaryMessenger.h"
|
||||||
#import "FlutterDartProject.h"
|
#import "FlutterDartProject.h"
|
||||||
|
#import "FlutterHourFormat.h"
|
||||||
#import "FlutterMacros.h"
|
#import "FlutterMacros.h"
|
||||||
#import "FlutterPluginRegistrarMacOS.h"
|
#import "FlutterPluginRegistrarMacOS.h"
|
||||||
#import "FlutterTexture.h"
|
#import "FlutterTexture.h"
|
||||||
|
@ -964,7 +964,7 @@ static void SetThreadPriority(FlutterThreadPriority priority) {
|
|||||||
@"platformBrightness" : [brightness isEqualToString:@"Dark"] ? @"dark" : @"light",
|
@"platformBrightness" : [brightness isEqualToString:@"Dark"] ? @"dark" : @"light",
|
||||||
// TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32006.
|
// TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32006.
|
||||||
@"textScaleFactor" : @1.0,
|
@"textScaleFactor" : @1.0,
|
||||||
@"alwaysUse24HourFormat" : @false
|
@"alwaysUse24HourFormat" : @([FlutterHourFormat isAlwaysUse24HourFormat]),
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1268,6 +1268,44 @@ TEST_F(FlutterEngineTest, DisplaySizeIsInPhysicalPixel) {
|
|||||||
engine = nil;
|
engine = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(FlutterEngineTest, ReportsHourFormat) {
|
||||||
|
__block BOOL expectedValue;
|
||||||
|
|
||||||
|
// Set up mocks.
|
||||||
|
id channelMock = OCMClassMock([FlutterBasicMessageChannel class]);
|
||||||
|
OCMStub([channelMock messageChannelWithName:@"flutter/settings"
|
||||||
|
binaryMessenger:[OCMArg any]
|
||||||
|
codec:[OCMArg any]])
|
||||||
|
.andReturn(channelMock);
|
||||||
|
OCMStub([channelMock sendMessage:[OCMArg any]]).andDo((^(NSInvocation* invocation) {
|
||||||
|
__weak id message;
|
||||||
|
[invocation getArgument:&message atIndex:2];
|
||||||
|
EXPECT_EQ(message[@"alwaysUse24HourFormat"], @(expectedValue));
|
||||||
|
}));
|
||||||
|
|
||||||
|
id mockHourFormat = OCMClassMock([FlutterHourFormat class]);
|
||||||
|
OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andDo((^(NSInvocation* invocation) {
|
||||||
|
[invocation setReturnValue:&expectedValue];
|
||||||
|
}));
|
||||||
|
|
||||||
|
id engineMock = CreateMockFlutterEngine(nil);
|
||||||
|
|
||||||
|
// Verify the YES case.
|
||||||
|
expectedValue = YES;
|
||||||
|
EXPECT_TRUE([engineMock runWithEntrypoint:@"main"]);
|
||||||
|
[engineMock shutDownEngine];
|
||||||
|
|
||||||
|
// Verify the NO case.
|
||||||
|
expectedValue = NO;
|
||||||
|
EXPECT_TRUE([engineMock runWithEntrypoint:@"main"]);
|
||||||
|
[engineMock shutDownEngine];
|
||||||
|
|
||||||
|
// Clean up mocks.
|
||||||
|
[mockHourFormat stopMocking];
|
||||||
|
[engineMock stopMocking];
|
||||||
|
[channelMock stopMocking];
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace flutter::testing
|
} // namespace flutter::testing
|
||||||
|
|
||||||
// NOLINTEND(clang-analyzer-core.StackAddressEscape)
|
// NOLINTEND(clang-analyzer-core.StackAddressEscape)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user