From fe206f528b769c5494a7f633c171f97156f1a2ba Mon Sep 17 00:00:00 2001 From: garyqian Date: Tue, 19 May 2020 20:01:21 -0700 Subject: [PATCH 1/7] Send platformResolvedLocale from iOS embedder --- .../ios/framework/Source/FlutterEngine.mm | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index ad00946028224..3138016ab3eb7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -759,27 +759,46 @@ - (void)setIsGpuDisabled:(BOOL)value { #pragma mark - Locale updates - (void)onLocaleUpdated:(NSNotification*)notification { - NSArray* preferredLocales = [NSLocale preferredLanguages]; - NSMutableArray* data = [[NSMutableArray new] autorelease]; + // [NSLocale currentLocale] provides an iOS resolved locale if the + // supported locales are exposed to the iOS embedder. Here, we get + // currentLocale and pass it to dart:ui + NSMutableArray* localeData = [[NSMutableArray new] autorelease]; + NSLocale* platformResolvedLocale = [NSLocale currentLocale]; + NSString* languageCode = [platformResolvedLocale objectForKey:NSLocaleLanguageCode]; + NSString* countryCode = [platformResolvedLocale objectForKey:NSLocaleCountryCode]; + NSString* scriptCode = [platformResolvedLocale objectForKey:NSLocaleScriptCode]; + NSString* variantCode = [platformResolvedLocale objectForKey:NSLocaleVariantCode]; + if (languageCode) { + [localeData addObject:languageCode]; + [localeData addObject:(countryCode ? countryCode : @"")]; + [localeData addObject:(scriptCode ? scriptCode : @"")]; + [localeData addObject:(variantCode ? variantCode : @"")]; + } + if (localeData.count != 0) { + [self.localizationChannel invokeMethod:@"setPlatformResolvedLocale" arguments:localeData]; + } + // Get and pass the user's perferred locale list to dart:ui + localeData = [[NSMutableArray new] autorelease]; + NSArray* preferredLocales = [NSLocale preferredLanguages]; for (NSString* localeID in preferredLocales) { - NSLocale* currentLocale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease]; - NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode]; - NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; - NSString* scriptCode = [currentLocale objectForKey:NSLocaleScriptCode]; - NSString* variantCode = [currentLocale objectForKey:NSLocaleVariantCode]; + NSLocale* locale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease]; + NSString* languageCode = [locale objectForKey:NSLocaleLanguageCode]; + NSString* countryCode = [locale objectForKey:NSLocaleCountryCode]; + NSString* scriptCode = [locale objectForKey:NSLocaleScriptCode]; + NSString* variantCode = [locale objectForKey:NSLocaleVariantCode]; if (!languageCode) { continue; } - [data addObject:languageCode]; - [data addObject:(countryCode ? countryCode : @"")]; - [data addObject:(scriptCode ? scriptCode : @"")]; - [data addObject:(variantCode ? variantCode : @"")]; + [localeData addObject:languageCode]; + [localeData addObject:(countryCode ? countryCode : @"")]; + [localeData addObject:(scriptCode ? scriptCode : @"")]; + [localeData addObject:(variantCode ? variantCode : @"")]; } - if (data.count == 0) { + if (localeData.count == 0) { return; } - [self.localizationChannel invokeMethod:@"setLocale" arguments:data]; + [self.localizationChannel invokeMethod:@"setLocale" arguments:localeData]; } @end From 2e27488f314262ff19b02c1fa0a947e598d98b3f Mon Sep 17 00:00:00 2001 From: garyqian Date: Wed, 20 May 2020 02:49:13 -0700 Subject: [PATCH 2/7] Add test --- .../LocalizationInitializationTest.m | 9 ++++- .../lib/src/locale_initialization.dart | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m index 974022ef6ff2e..8063a6429293e 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m @@ -25,11 +25,16 @@ - (void)setUp { - (void)testNoLocalePrepend { NSTimeInterval timeout = 10.0; + // 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. XCUIElement* textInputSemanticsObject = [self.application.textFields matchingIdentifier:@"[en]"].element; + XCTAssertTrue([textInputSemanticsObject waitForExistenceWithTimeout:timeout]); - // 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. + [textInputSemanticsObject tap]; + + // [NSLocale currentLocale] always includes a country code. + textInputSemanticsObject = [self.application.textFields matchingIdentifier:@"en_US"].element; XCTAssertTrue([textInputSemanticsObject waitForExistenceWithTimeout:timeout]); } diff --git a/testing/scenario_app/lib/src/locale_initialization.dart b/testing/scenario_app/lib/src/locale_initialization.dart index f20985eb215e8..3658287afd71d 100644 --- a/testing/scenario_app/lib/src/locale_initialization.dart +++ b/testing/scenario_app/lib/src/locale_initialization.dart @@ -16,6 +16,8 @@ class LocaleInitialization extends Scenario { : assert(window != null), super(window); + int tapCount = 0; + @override void onBeginFrame(Duration duration) { // Doesn't matter what we draw. Just paint white. @@ -66,4 +68,41 @@ class LocaleInitialization extends Scenario { )).build() ); } + + // We don't really care about the touch itself. It's just a way for the + // XCUITest to communicate timing to the mock framework. + @override + void onPointerDataPacket(PointerDataPacket packet) { + String label; + switch(tapCount) { + case 0: label = window.platformResolvedLocale.toString(); break; + // Expand for other test cases. + } + + window.updateSemantics((SemanticsUpdateBuilder() + ..updateNode( + id: 0, + // SemanticsFlag.isTextField and SemanticsFlag.isFocused. + flags: 48, + actions: 18433, + rect: const Rect.fromLTRB(0.0, 0.0, 414.0, 48.0), + label: label, + textDirection: TextDirection.ltr, + textSelectionBase: 0, + textSelectionExtent: 0, + 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() + ); + tapCount++; + } } From 096e302c380a0f9e1150b875029192b68693ffa6 Mon Sep 17 00:00:00 2001 From: garyqian Date: Wed, 20 May 2020 03:14:34 -0700 Subject: [PATCH 3/7] Kick tests From def5ce4b2461eea4843c4b5eb00a9800a539ad57 Mon Sep 17 00:00:00 2001 From: garyqian Date: Wed, 20 May 2020 10:18:47 -0700 Subject: [PATCH 4/7] Analyzer --- testing/scenario_app/lib/src/locale_initialization.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/testing/scenario_app/lib/src/locale_initialization.dart b/testing/scenario_app/lib/src/locale_initialization.dart index 3658287afd71d..3b9a14508ddbb 100644 --- a/testing/scenario_app/lib/src/locale_initialization.dart +++ b/testing/scenario_app/lib/src/locale_initialization.dart @@ -69,8 +69,9 @@ class LocaleInitialization extends Scenario { ); } - // We don't really care about the touch itself. It's just a way for the - // XCUITest to communicate timing to the mock framework. + /// Handle taps. + /// + /// Send changing information via semantics on each successive tap. @override void onPointerDataPacket(PointerDataPacket packet) { String label; From 72e17fc4a75b157d637fd9ac20280558a347d4de Mon Sep 17 00:00:00 2001 From: garyqian Date: Wed, 20 May 2020 14:13:25 -0700 Subject: [PATCH 5/7] Private vars --- testing/scenario_app/lib/src/locale_initialization.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/testing/scenario_app/lib/src/locale_initialization.dart b/testing/scenario_app/lib/src/locale_initialization.dart index 3b9a14508ddbb..ee8111dd53822 100644 --- a/testing/scenario_app/lib/src/locale_initialization.dart +++ b/testing/scenario_app/lib/src/locale_initialization.dart @@ -16,8 +16,9 @@ class LocaleInitialization extends Scenario { : assert(window != null), super(window); - int tapCount = 0; + int _tapCount = 0; + /// Start off by sending the supported locales list via semantics. @override void onBeginFrame(Duration duration) { // Doesn't matter what we draw. Just paint white. @@ -75,7 +76,7 @@ class LocaleInitialization extends Scenario { @override void onPointerDataPacket(PointerDataPacket packet) { String label; - switch(tapCount) { + switch(_tapCount) { case 0: label = window.platformResolvedLocale.toString(); break; // Expand for other test cases. } @@ -104,6 +105,6 @@ class LocaleInitialization extends Scenario { additionalActions: Int32List(0), )).build() ); - tapCount++; + _tapCount++; } } From 7791d6de3a4aefeb3a372ef7388192680e13601f Mon Sep 17 00:00:00 2001 From: garyqian Date: Wed, 20 May 2020 14:49:12 -0700 Subject: [PATCH 6/7] Use proper index --- testing/scenario_app/lib/src/locale_initialization.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/testing/scenario_app/lib/src/locale_initialization.dart b/testing/scenario_app/lib/src/locale_initialization.dart index ee8111dd53822..2f4a998e0c093 100644 --- a/testing/scenario_app/lib/src/locale_initialization.dart +++ b/testing/scenario_app/lib/src/locale_initialization.dart @@ -77,16 +77,17 @@ class LocaleInitialization extends Scenario { void onPointerDataPacket(PointerDataPacket packet) { String label; switch(_tapCount) { - case 0: label = window.platformResolvedLocale.toString(); break; + case 1: label = window.platformResolvedLocale.toString(); break; // Expand for other test cases. } window.updateSemantics((SemanticsUpdateBuilder() ..updateNode( id: 0, - // SemanticsFlag.isTextField and SemanticsFlag.isFocused. - flags: 48, - actions: 18433, + // SemanticsFlag.isTextField. + flags: 16, + // SemanticsAction.tap. + actions: 1, rect: const Rect.fromLTRB(0.0, 0.0, 414.0, 48.0), label: label, textDirection: TextDirection.ltr, From c401fab51145c99278cf8a34339de21ba48e630c Mon Sep 17 00:00:00 2001 From: garyqian Date: Wed, 20 May 2020 14:58:02 -0700 Subject: [PATCH 7/7] Address comments --- shell/platform/darwin/ios/framework/Source/FlutterEngine.mm | 2 +- .../ScenariosUITests/LocalizationInitializationTest.m | 5 +++-- testing/scenario_app/lib/src/locale_initialization.dart | 5 ++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 3138016ab3eb7..59eae779c612d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -778,7 +778,7 @@ - (void)onLocaleUpdated:(NSNotification*)notification { [self.localizationChannel invokeMethod:@"setPlatformResolvedLocale" arguments:localeData]; } - // Get and pass the user's perferred locale list to dart:ui + // Get and pass the user's preferred locale list to dart:ui localeData = [[NSMutableArray new] autorelease]; NSArray* preferredLocales = [NSLocale preferredLanguages]; for (NSString* localeID in preferredLocales) { diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m index 8063a6429293e..3648eb9caa57f 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m @@ -25,8 +25,9 @@ - (void)setUp { - (void)testNoLocalePrepend { NSTimeInterval timeout = 10.0; - // 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. + // The locales received by dart:ui are exposed onBeginFrame via semantics label. + // There should only be one locale, since the default iOS app only has en_US as + // the locale. The list should consist of just the en locale. XCUIElement* textInputSemanticsObject = [self.application.textFields matchingIdentifier:@"[en]"].element; XCTAssertTrue([textInputSemanticsObject waitForExistenceWithTimeout:timeout]); diff --git a/testing/scenario_app/lib/src/locale_initialization.dart b/testing/scenario_app/lib/src/locale_initialization.dart index 2f4a998e0c093..e19bc418a98b3 100644 --- a/testing/scenario_app/lib/src/locale_initialization.dart +++ b/testing/scenario_app/lib/src/locale_initialization.dart @@ -77,7 +77,10 @@ class LocaleInitialization extends Scenario { void onPointerDataPacket(PointerDataPacket packet) { String label; switch(_tapCount) { - case 1: label = window.platformResolvedLocale.toString(); break; + case 1: { + label = window.platformResolvedLocale.toString(); + break; + } // Expand for other test cases. }