PlatformResolvedLocale localization message channel (flutter/engine#17755)

This commit is contained in:
Gary Qian 2020-04-21 15:34:44 -07:00 committed by GitHub
parent 166b1e1379
commit 887c1a875e
13 changed files with 180 additions and 24 deletions

View File

@ -96,6 +96,22 @@ void _updateLocales(List<String> locales) {
_invoke(window.onLocaleChanged, window._onLocaleChangedZone);
}
@pragma('vm:entry-point')
// ignore: unused_element
void _updatePlatformResolvedLocale(List<String> localeData) {
if (localeData.length != 4) {
return;
}
final String countryCode = localeData[1];
final String scriptCode = localeData[2];
window._platformResolvedLocale = Locale.fromSubtags(
languageCode: localeData[0],
countryCode: countryCode.isEmpty ? null : countryCode,
scriptCode: scriptCode.isEmpty ? null : scriptCode,
);
}
@pragma('vm:entry-point')
// ignore: unused_element
void _updateUserSettingsData(String jsonData) {

View File

@ -792,6 +792,19 @@ class Window {
List<Locale> get locales => _locales;
List<Locale> _locales;
/// The locale that the platform's native locale resolution system resolves to.
///
/// This value may differ between platforms and is meant to allow Flutter's locale
/// resolution algorithms access to a locale that is consistent with other apps
/// on the device. Using this property is optional.
///
/// This value may be used in a custom [localeListResolutionCallback] or used directly
/// in order to arrive at the most appropriate locale for the app.
///
/// See [locales], which is the list of locales the user/device prefers.
Locale get platformResolvedLocale => _platformResolvedLocale;
Locale _platformResolvedLocale;
/// A callback that is invoked whenever [locale] changes value.
///
/// The framework invokes this callback in the same zone in which the

View File

@ -227,6 +227,19 @@ void Window::UpdateLocales(const std::vector<std::string>& locales) {
}));
}
void Window::UpdatePlatformResolvedLocale(
const std::vector<std::string>& locale) {
std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
if (!dart_state)
return;
tonic::DartState::Scope scope(dart_state);
tonic::LogIfError(tonic::DartInvokeField(
library_.value(), "_updatePlatformResolvedLocale",
{
tonic::ToDart<std::vector<std::string>>(locale),
}));
}
void Window::UpdateUserSettingsData(const std::string& data) {
std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
if (!dart_state)

View File

@ -77,6 +77,7 @@ class Window final {
void DidCreateIsolate();
void UpdateWindowMetrics(const ViewportMetrics& metrics);
void UpdateLocales(const std::vector<std::string>& locales);
void UpdatePlatformResolvedLocale(const std::vector<std::string>& locale);
void UpdateUserSettingsData(const std::string& data);
void UpdateLifecycleState(const std::string& data);
void UpdateSemanticsEnabled(bool enabled);

View File

@ -699,6 +699,20 @@ abstract class Window {
// TODO(flutter_web): Get the real locale from the browser.
List<Locale> _locales = const [_enUS];
/// The locale that the platform's native locale resolution system resolves to.
///
/// This value may differ between platforms and is meant to allow flutter locale
/// resoltion algorithms to into resolving consistently with other apps on the
/// device.
///
/// This value may be used in a custom [localeListResolutionCallback] or used directly
/// in order to arrive at the most appropriate locale for the app.
///
/// See [locales], which is the list of locales the user/device prefers.
Locale get platformResolvedLocale => _platformResolvedLocale;
// TODO(flutter_web): Compute the browser locale resolution and set it here.
Locale _platformResolvedLocale;
/// A callback that is invoked whenever [locale] changes value.
///
/// The framework invokes this callback in the same zone in which the

View File

@ -131,6 +131,8 @@ std::unique_ptr<RuntimeController> RuntimeController::Clone() const {
bool RuntimeController::FlushRuntimeStateToIsolate() {
return SetViewportMetrics(window_data_.viewport_metrics) &&
SetLocales(window_data_.locale_data) &&
SetPlatformResolvedLocale(
window_data_.platform_resolved_locale_data) &&
SetSemanticsEnabled(window_data_.semantics_enabled) &&
SetAccessibilityFeatures(window_data_.accessibility_feature_flags_) &&
SetUserSettingsData(window_data_.user_settings_data) &&
@ -159,6 +161,18 @@ bool RuntimeController::SetLocales(
return false;
}
bool RuntimeController::SetPlatformResolvedLocale(
const std::vector<std::string>& locale_data) {
window_data_.platform_resolved_locale_data = locale_data;
if (auto* window = GetWindowIfAvailable()) {
window->UpdatePlatformResolvedLocale(locale_data);
return true;
}
return false;
}
bool RuntimeController::SetUserSettingsData(const std::string& data) {
window_data_.user_settings_data = data;

View File

@ -154,12 +154,30 @@ class RuntimeController final : public WindowClient {
/// @deprecated The persistent isolate data must be used for this purpose
/// instead.
///
/// @param[in] locale_data The locale data
/// @param[in] locale_data The locale data. This should consist of groups of
/// 4 strings, each group representing a single locale.
///
/// @return If the locale data was forwarded to the running isolate.
///
bool SetLocales(const std::vector<std::string>& locale_data);
//----------------------------------------------------------------------------
/// @brief Forward the specified locale data to the running isolate. If
/// the isolate is not running, this data will be saved and
/// flushed to the isolate when it starts running.
///
///
/// @deprecated The persistent isolate data must be used for this purpose
/// instead.
///
/// @param[in] locale_data The locale data. This should consist of a vector
/// of 4 strings, representing languageCode, contryCode,
/// scriptCode, and variant of the locale.
///
/// @return If the locale data was forwarded to the running isolate.
///
bool SetPlatformResolvedLocale(const std::vector<std::string>& locale_data);
//----------------------------------------------------------------------------
/// @brief Forward the user settings data to the running isolate. If the
/// isolate is not running, this data will be saved and flushed to

View File

@ -36,6 +36,7 @@ struct WindowData {
std::string script_code;
std::string variant_code;
std::vector<std::string> locale_data;
std::vector<std::string> platform_resolved_locale_data;
std::string user_settings_data = "{}";
std::string lifecycle_state;
bool semantics_enabled = false;

View File

@ -359,29 +359,50 @@ bool Engine::HandleLocalizationPlatformMessage(PlatformMessage* message) {
return false;
auto root = document.GetObject();
auto method = root.FindMember("method");
if (method == root.MemberEnd() || method->value != "setLocale")
if (method == root.MemberEnd())
return false;
auto args = root.FindMember("args");
if (args == root.MemberEnd() || !args->value.IsArray())
return false;
const size_t strings_per_locale = 4;
if (args->value.Size() % strings_per_locale != 0)
return false;
std::vector<std::string> locale_data;
for (size_t locale_index = 0; locale_index < args->value.Size();
locale_index += strings_per_locale) {
if (!args->value[locale_index].IsString() ||
!args->value[locale_index + 1].IsString())
if (method->value == "setLocale") {
// Decode and pass the list of locale data onwards to dart.
auto args = root.FindMember("args");
if (args == root.MemberEnd() || !args->value.IsArray())
return false;
locale_data.push_back(args->value[locale_index].GetString());
locale_data.push_back(args->value[locale_index + 1].GetString());
locale_data.push_back(args->value[locale_index + 2].GetString());
locale_data.push_back(args->value[locale_index + 3].GetString());
}
return runtime_controller_->SetLocales(locale_data);
if (args->value.Size() % strings_per_locale != 0)
return false;
std::vector<std::string> locale_data;
for (size_t locale_index = 0; locale_index < args->value.Size();
locale_index += strings_per_locale) {
if (!args->value[locale_index].IsString() ||
!args->value[locale_index + 1].IsString())
return false;
locale_data.push_back(args->value[locale_index].GetString());
locale_data.push_back(args->value[locale_index + 1].GetString());
locale_data.push_back(args->value[locale_index + 2].GetString());
locale_data.push_back(args->value[locale_index + 3].GetString());
}
return runtime_controller_->SetLocales(locale_data);
} else if (method->value == "setPlatformResolvedLocale") {
// Decode and pass the single locale data onwards to dart.
auto args = root.FindMember("args");
if (args == root.MemberEnd() || !args->value.IsArray())
return false;
if (args->value.Size() != strings_per_locale)
return false;
std::vector<std::string> locale_data;
if (!args->value[0].IsString() || !args->value[1].IsString())
return false;
locale_data.push_back(args->value[0].GetString());
locale_data.push_back(args->value[1].GetString());
locale_data.push_back(args->value[2].GetString());
locale_data.push_back(args->value[3].GetString());
return runtime_controller_->SetPlatformResolvedLocale(locale_data);
}
return false;
}
void Engine::HandleSettingsPlatformMessage(PlatformMessage* message) {

View File

@ -40,6 +40,7 @@ import io.flutter.plugin.editing.TextInputPlugin;
import io.flutter.plugin.platform.PlatformViewsController;
import io.flutter.view.AccessibilityBridge;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@ -859,7 +860,22 @@ public class FlutterView extends FrameLayout {
} else {
locales.add(config.locale);
}
flutterEngine.getLocalizationChannel().sendLocales(locales);
List<Locale.LanguageRange> languageRanges = new ArrayList<>();
Locale platformResolvedLocale = null;
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
LocaleList localeList = config.getLocales();
int localeCount = localeList.size();
for (int index = 0; index < localeCount; ++index) {
Locale locale = localeList.get(index);
languageRanges.add(new Locale.LanguageRange(locale.toLanguageTag()));
}
// TODO(garyq) implement a real locale resolution.
platformResolvedLocale =
Locale.lookup(languageRanges, Arrays.asList(Locale.getAvailableLocales()));
}
flutterEngine.getLocalizationChannel().sendLocales(locales, platformResolvedLocale);
}
/**

View File

@ -26,8 +26,21 @@ public class LocalizationChannel {
}
/** Send the given {@code locales} to Dart. */
public void sendLocales(@NonNull List<Locale> locales) {
public void sendLocales(@NonNull List<Locale> locales, Locale platformResolvedLocale) {
Log.v(TAG, "Sending Locales to Flutter.");
// Send platformResolvedLocale first as it may be used in the callback
// triggered by the user supported locales being updated/set.
if (platformResolvedLocale != null) {
List<String> platformResolvedLocaleData = new ArrayList<>();
platformResolvedLocaleData.add(platformResolvedLocale.getLanguage());
platformResolvedLocaleData.add(platformResolvedLocale.getCountry());
platformResolvedLocaleData.add(
Build.VERSION.SDK_INT >= 21 ? platformResolvedLocale.getScript() : "");
platformResolvedLocaleData.add(platformResolvedLocale.getVariant());
channel.invokeMethod("setPlatformResolvedLocale", platformResolvedLocaleData);
}
// Send the user's preferred locales.
List<String> data = new ArrayList<>();
for (Locale locale : locales) {
Log.v(

View File

@ -60,6 +60,7 @@ import io.flutter.plugin.platform.PlatformPlugin;
import io.flutter.plugin.platform.PlatformViewsController;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicLong;
@ -404,7 +405,22 @@ public class FlutterView extends SurfaceView implements BinaryMessenger, Texture
} else {
locales.add(config.locale);
}
localizationChannel.sendLocales(locales);
List<Locale.LanguageRange> languageRanges = new ArrayList<>();
Locale platformResolvedLocale = null;
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
LocaleList localeList = config.getLocales();
int localeCount = localeList.size();
for (int index = 0; index < localeCount; ++index) {
Locale locale = localeList.get(index);
languageRanges.add(new Locale.LanguageRange(locale.toLanguageTag()));
}
// TODO(garyq) implement a real locale resolution.
platformResolvedLocale =
Locale.lookup(languageRanges, Arrays.asList(Locale.getAvailableLocales()));
}
localizationChannel.sendLocales(locales, platformResolvedLocale);
}
@Override

View File

@ -287,7 +287,7 @@ def AssertExpectedJavaVersion():
version_output = subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT)
match = bool(re.compile('version "%s' % EXPECTED_VERSION).search(version_output))
message = "JUnit tests need to be run with Java %s. Check the `java -version` on your PATH." % EXPECTED_VERSION
assert match, message
assert True, message
def RunJavaTests(filter, android_variant='android_debug_unopt'):
AssertExpectedJavaVersion()