diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 6c79449795164..ad00946028224 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -762,22 +762,6 @@ - (void)onLocaleUpdated:(NSNotification*)notification { NSArray* preferredLocales = [NSLocale preferredLanguages]; NSMutableArray* data = [[NSMutableArray new] autorelease]; - // Force prepend the [NSLocale currentLocale] to the front of the list - // to ensure we are including the full default locale. preferredLocales - // is not guaranteed to include anything beyond the languageCode. - NSLocale* currentLocale = [NSLocale currentLocale]; - NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode]; - NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; - NSString* scriptCode = [currentLocale objectForKey:NSLocaleScriptCode]; - NSString* variantCode = [currentLocale objectForKey:NSLocaleVariantCode]; - if (languageCode) { - [data addObject:languageCode]; - [data addObject:(countryCode ? countryCode : @"")]; - [data addObject:(scriptCode ? scriptCode : @"")]; - [data addObject:(variantCode ? variantCode : @"")]; - } - - // Add any secondary locales/languages to the list. for (NSString* localeID in preferredLocales) { NSLocale* currentLocale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease]; NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode]; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index b0c4a579e85f4..f24db6504742a 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -42,6 +42,7 @@ 3DEF491823C3BE6500184216 /* golden_platform_view_opacity_iPhone 8_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 3DE09E8F23C010BD006C9851 /* golden_platform_view_opacity_iPhone 8_simulator.png */; }; 3DEF491923C3BE6500184216 /* golden_platform_view_rotate_iPhone 8_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 3DE09E8E23C010BD006C9851 /* golden_platform_view_rotate_iPhone 8_simulator.png */; }; 3DEF491A23C3BE6500184216 /* golden_platform_view_transform_iPhone 8_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 3DE09E9123C010BD006C9851 /* golden_platform_view_transform_iPhone 8_simulator.png */; }; + 4F06F1B32473296E000AF246 /* LocalizationInitializationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F06F1B124731F66000AF246 /* LocalizationInitializationTest.m */; }; 59A97FD8236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */; }; 59A97FDA236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */; }; 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */; }; @@ -151,6 +152,7 @@ 3DE09E9023C010BD006C9851 /* golden_platform_view_iPhone 8_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_iPhone 8_simulator.png"; sourceTree = ""; }; 3DE09E9123C010BD006C9851 /* golden_platform_view_transform_iPhone 8_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_transform_iPhone 8_simulator.png"; sourceTree = ""; }; 3DE09E9223C010BD006C9851 /* golden_platform_view_cliprect_iPhone 8_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone 8_simulator.png"; sourceTree = ""; }; + 4F06F1B124731F66000AF246 /* LocalizationInitializationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalizationInitializationTest.m; sourceTree = ""; }; 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_iPhone SE_simulator.png"; sourceTree = ""; }; 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png"; sourceTree = ""; }; 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnobstructedPlatformViewTests.m; sourceTree = ""; }; @@ -250,6 +252,7 @@ 248D76ED22E388380012F0C1 /* ScenariosUITests */ = { isa = PBXGroup; children = ( + 4F06F1B124731F66000AF246 /* LocalizationInitializationTest.m */, 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */, 0D14A3FD239743190013D873 /* golden_platform_view_rotate_iPhone SE_simulator.png */, 3DE09E8B23C010BC006C9851 /* golden_platform_view_clippath_iPhone 8_simulator.png */, @@ -503,6 +506,7 @@ 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */, 248D76EF22E388380012F0C1 /* PlatformViewUITests.m in Sources */, 0D8470A4240F0B1F0030B565 /* StatusBarTest.m in Sources */, + 4F06F1B32473296E000AF246 /* LocalizationInitializationTest.m in Sources */, 0A42BFB42447E179007E212E /* TextSemanticsFocusTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 979887507f9f4..d09cc4db033c4 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -26,6 +26,7 @@ - (BOOL)application:(UIApplication*)application NSDictionary* launchArgsMap = @{ // The Platform view golden test args should match `PlatformViewGoldenTestManager`. + @"--locale-initialization" : @"locale_initialization", @"--platform-view" : @"platform_view", @"--platform-view-no-overlay-intersection" : @"platform_view_no_overlay_intersection", @"--platform-view-two-intersecting-overlays" : @"platform_view_two_intersecting_overlays", diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m new file mode 100644 index 0000000000000..974022ef6ff2e --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m @@ -0,0 +1,36 @@ +// 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 +#import + +FLUTTER_ASSERT_ARC + +@interface LocalizationInitializationTest : XCTestCase +@property(nonatomic, strong) XCUIApplication* application; +@end + +@implementation LocalizationInitializationTest + +- (void)setUp { + [super setUp]; + self.continueAfterFailure = NO; + + self.application = [[XCUIApplication alloc] init]; + self.application.launchArguments = @[ @"--locale-initialization" ]; + [self.application launch]; +} + +- (void)testNoLocalePrepend { + NSTimeInterval timeout = 10.0; + + XCUIElement* textInputSemanticsObject = + [self.application.textFields matchingIdentifier:@"[en]"].element; + + // The locales recieved by dart:ui are exposed onBeginFrame via semantics label. + // There should only be one locale, as we have removed the locale prepend on iOS. + XCTAssertTrue([textInputSemanticsObject waitForExistenceWithTimeout:timeout]); +} + +@end diff --git a/testing/scenario_app/lib/src/locale_initialization.dart b/testing/scenario_app/lib/src/locale_initialization.dart new file mode 100644 index 0000000000000..f20985eb215e8 --- /dev/null +++ b/testing/scenario_app/lib/src/locale_initialization.dart @@ -0,0 +1,69 @@ +// Copyright 2020 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 'dart:typed_data'; +import 'dart:ui'; + +import 'package:vector_math/vector_math_64.dart'; + +import 'scenario.dart'; + +/// Sends the recieved locale data back as semantics information. +class LocaleInitialization extends Scenario { + /// Constructor + LocaleInitialization(Window window) + : assert(window != null), + super(window); + + @override + void onBeginFrame(Duration duration) { + // Doesn't matter what we draw. Just paint white. + final SceneBuilder builder = SceneBuilder(); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + + canvas.drawRect( + Rect.fromLTWH(0, 0, window.physicalSize.width, window.physicalSize.height), + Paint()..color = const Color.fromARGB(255, 255, 255, 255), + ); + final Picture picture = recorder.endRecording(); + + builder.addPicture( + Offset.zero, + picture, + ); + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + + // On the first frame, pretend that it drew a text field. Send the + // corresponding semantics tree comprised of 1 node with the locale data + // as the label. + window.updateSemantics((SemanticsUpdateBuilder() + ..updateNode( + id: 0, + // SemanticsFlag.isTextField. + flags: 16, + // SemanticsAction.tap. + actions: 1, + rect: const Rect.fromLTRB(0.0, 0.0, 414.0, 48.0), + label: window.locales.toString(), + textDirection: TextDirection.ltr, + textSelectionBase: -1, + textSelectionExtent: -1, + platformViewId: -1, + maxValueLength: -1, + currentValueLength: 0, + scrollChildren: 0, + scrollIndex: 0, + transform: Matrix4.identity().storage, + elevation: 0.0, + thickness: 0.0, + childrenInTraversalOrder: Int32List(0), + childrenInHitTestOrder: Int32List(0), + additionalActions: Int32List(0), + )).build() + ); + } +} diff --git a/testing/scenario_app/lib/src/scenarios.dart b/testing/scenario_app/lib/src/scenarios.dart index dbfe793e1a21f..861c71a354bfc 100644 --- a/testing/scenario_app/lib/src/scenarios.dart +++ b/testing/scenario_app/lib/src/scenarios.dart @@ -6,6 +6,7 @@ import 'dart:ui'; import 'animated_color_square.dart'; +import 'locale_initialization.dart'; import 'platform_view.dart'; import 'poppable_screen.dart'; import 'scenario.dart'; @@ -14,6 +15,7 @@ import 'touches_scenario.dart'; Map _scenarios = { 'animated_color_square': AnimatedColorSquareScenario(window), + 'locale_initialization': LocaleInitialization(window), 'platform_view': PlatformViewScenario(window, 'Hello from Scenarios (Platform View)', id: 0), 'platform_view_no_overlay_intersection': PlatformViewNoOverlayIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), 'platform_view_partial_intersection': PlatformViewPartialIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0),