mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add alwaysUse24HourFormat and textScaleFactor (flutter/engine#4202)
* systems/settings channel split * merge textScaleFactor and alwaysUse24HourFormat into flutter/settings channel * add debugOverrideAlwaysUse24HourFormat * implement textScaleFactor on iOS * address comments * remove debugOverrideAlwaysUse24HourFormat * clang-format
This commit is contained in:
parent
268fa25fff
commit
db7ec2ce7e
@ -32,11 +32,21 @@ void _updateLocale(String languageCode, String countryCode) {
|
||||
_invoke(window.onLocaleChanged, window._onLocaleChangedZone);
|
||||
}
|
||||
|
||||
void _updateUserSettingsData(String json) {
|
||||
final Map<String, dynamic> data = JSON.decode(json);
|
||||
_updateTextScaleFactor(data['textScaleFactor'].toDouble());
|
||||
_updateAlwaysUse24HourFormat(data['alwaysUse24HourFormat']);
|
||||
}
|
||||
|
||||
void _updateTextScaleFactor(double textScaleFactor) {
|
||||
window._textScaleFactor = textScaleFactor;
|
||||
_invoke(window.onTextScaleFactorChanged, window._onTextScaleFactorChangedZone);
|
||||
}
|
||||
|
||||
void _updateAlwaysUse24HourFormat(bool alwaysUse24HourFormat) {
|
||||
window._alwaysUse24HourFormat = alwaysUse24HourFormat;
|
||||
}
|
||||
|
||||
void _updateSemanticsEnabled(bool enabled) {
|
||||
window._semanticsEnabled = enabled;
|
||||
_invoke(window.onSemanticsEnabledChanged, window._onSemanticsEnabledChangedZone);
|
||||
|
||||
@ -269,6 +269,13 @@ class Window {
|
||||
double get textScaleFactor => _textScaleFactor;
|
||||
double _textScaleFactor = 1.0;
|
||||
|
||||
/// The setting indicating whether time should always be shown in the 24-hour
|
||||
/// format.
|
||||
///
|
||||
/// This option is used by [showTimePicker].
|
||||
bool get alwaysUse24HourFormat => _alwaysUse24HourFormat;
|
||||
bool _alwaysUse24HourFormat = false;
|
||||
|
||||
/// A callback that is invoked whenever [textScaleFactor] changes value.
|
||||
///
|
||||
/// The framework invokes this callback in the same zone in which the
|
||||
|
||||
@ -163,15 +163,15 @@ void Window::UpdateLocale(const std::string& language_code,
|
||||
});
|
||||
}
|
||||
|
||||
void Window::UpdateTextScaleFactor(double text_scale_factor) {
|
||||
void Window::UpdateUserSettingsData(const std::string& data) {
|
||||
tonic::DartState* dart_state = library_.dart_state().get();
|
||||
if (!dart_state)
|
||||
return;
|
||||
tonic::DartState::Scope scope(dart_state);
|
||||
|
||||
DartInvokeField(library_.value(), "_updateTextScaleFactor",
|
||||
DartInvokeField(library_.value(), "_updateUserSettingsData",
|
||||
{
|
||||
ToDart(static_cast<double>(text_scale_factor)),
|
||||
StdStringToDart(data),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@ class Window {
|
||||
void UpdateWindowMetrics(const ViewportMetrics& metrics);
|
||||
void UpdateLocale(const std::string& language_code,
|
||||
const std::string& country_code);
|
||||
void UpdateTextScaleFactor(double text_scale_factor);
|
||||
void UpdateUserSettingsData(const std::string& data);
|
||||
void UpdateSemanticsEnabled(bool enabled);
|
||||
void DispatchPlatformMessage(fxl::RefPtr<PlatformMessage> message);
|
||||
void DispatchPointerDataPacket(const PointerDataPacket& packet);
|
||||
|
||||
@ -66,11 +66,11 @@ void RuntimeController::SetLocale(const std::string& language_code,
|
||||
GetWindow()->UpdateLocale(language_code_, country_code_);
|
||||
}
|
||||
|
||||
void RuntimeController::SetTextScaleFactor(double text_scale_factor) {
|
||||
if (text_scale_factor_ == text_scale_factor)
|
||||
void RuntimeController::SetUserSettingsData(const std::string& data) {
|
||||
if (user_settings_data_ == data)
|
||||
return;
|
||||
text_scale_factor_ = text_scale_factor;
|
||||
GetWindow()->UpdateTextScaleFactor(text_scale_factor_);
|
||||
user_settings_data_ = data;
|
||||
GetWindow()->UpdateUserSettingsData(user_settings_data_);
|
||||
}
|
||||
|
||||
void RuntimeController::SetSemanticsEnabled(bool enabled) {
|
||||
|
||||
@ -35,7 +35,7 @@ class RuntimeController : public WindowClient, public IsolateClient {
|
||||
void SetViewportMetrics(const ViewportMetrics& metrics);
|
||||
void SetLocale(const std::string& language_code,
|
||||
const std::string& country_code);
|
||||
void SetTextScaleFactor(double textScaleFactor);
|
||||
void SetUserSettingsData(const std::string& data);
|
||||
void SetSemanticsEnabled(bool enabled);
|
||||
|
||||
void BeginFrame(fxl::TimePoint frame_time);
|
||||
@ -66,7 +66,7 @@ class RuntimeController : public WindowClient, public IsolateClient {
|
||||
RuntimeDelegate* client_;
|
||||
std::string language_code_;
|
||||
std::string country_code_;
|
||||
double text_scale_factor_ = 1.0;
|
||||
std::string user_settings_data_ = "{}";
|
||||
bool semantics_enabled_ = false;
|
||||
std::unique_ptr<DartController> dart_controller_;
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ constexpr char kAssetChannel[] = "flutter/assets";
|
||||
constexpr char kLifecycleChannel[] = "flutter/lifecycle";
|
||||
constexpr char kNavigationChannel[] = "flutter/navigation";
|
||||
constexpr char kLocalizationChannel[] = "flutter/localization";
|
||||
constexpr char kSystemChannel[] = "flutter/system";
|
||||
constexpr char kSettingsChannel[] = "flutter/settings";
|
||||
|
||||
bool PathExists(const std::string& path) {
|
||||
return access(path.c_str(), R_OK) == 0;
|
||||
@ -75,7 +75,7 @@ Engine::Engine(PlatformView* platform_view)
|
||||
platform_view->GetVsyncWaiter(),
|
||||
this)),
|
||||
load_script_error_(tonic::kNoError),
|
||||
text_scale_factor_(1.0),
|
||||
user_settings_data_("{}"),
|
||||
activity_running_(false),
|
||||
have_surface_(false),
|
||||
weak_factory_(this) {}
|
||||
@ -337,11 +337,9 @@ void Engine::DispatchPlatformMessage(
|
||||
} else if (message->channel() == kLocalizationChannel) {
|
||||
if (HandleLocalizationPlatformMessage(message.get()))
|
||||
return;
|
||||
} else if (message->channel() == kSystemChannel) {
|
||||
// This only handles textScaleFactor changes: other system messages are
|
||||
// handled by DispatchPlatformMessage below.
|
||||
if (HandleSystemPlatformMessage(message.get()))
|
||||
return;
|
||||
} else if (message->channel() == kSettingsChannel) {
|
||||
HandleSettingsPlatformMessage(message.get());
|
||||
return;
|
||||
}
|
||||
|
||||
if (runtime_) {
|
||||
@ -424,35 +422,15 @@ bool Engine::HandleLocalizationPlatformMessage(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Engine::HandleSystemPlatformMessage(blink::PlatformMessage* message) {
|
||||
void Engine::HandleSettingsPlatformMessage(blink::PlatformMessage* message) {
|
||||
const auto& data = message->data();
|
||||
rapidjson::Document document;
|
||||
document.Parse(reinterpret_cast<const char*>(data.data()), data.size());
|
||||
|
||||
if (document.HasParseError() || !document.IsObject())
|
||||
return false;
|
||||
|
||||
auto root = document.GetObject();
|
||||
auto type = root.FindMember("type");
|
||||
if (type == root.MemberEnd() || type->value != "systemSettings")
|
||||
return false;
|
||||
|
||||
// This only handles textScaleFactor changes: other system messages
|
||||
// are handled by DispatchPlatformMessage.
|
||||
auto text_scale_factor = root.FindMember("textScaleFactor");
|
||||
if (text_scale_factor == root.MemberEnd() ||
|
||||
!text_scale_factor->value.IsDouble()) {
|
||||
return false;
|
||||
}
|
||||
text_scale_factor_ = text_scale_factor->value.GetDouble();
|
||||
std::string jsonData(reinterpret_cast<const char*>(data.data()), data.size());
|
||||
user_settings_data_ = jsonData;
|
||||
if (runtime_) {
|
||||
runtime_->SetTextScaleFactor(text_scale_factor_);
|
||||
runtime_->SetUserSettingsData(user_settings_data_);
|
||||
if (have_surface_)
|
||||
ScheduleFrame();
|
||||
}
|
||||
// If the only members were "type" and "textScaleFactor", then we're done.
|
||||
// If there are more members, then we need to send it on to other handlers.
|
||||
return root.MemberCount() == 2;
|
||||
}
|
||||
|
||||
void Engine::DispatchPointerDataPacket(const PointerDataPacket& packet) {
|
||||
@ -507,7 +485,7 @@ void Engine::ConfigureRuntime(const std::string& script_uri,
|
||||
default_isolate_snapshot_instr, platform_kernel);
|
||||
runtime_->SetViewportMetrics(viewport_metrics_);
|
||||
runtime_->SetLocale(language_code_, country_code_);
|
||||
runtime_->SetTextScaleFactor(text_scale_factor_);
|
||||
runtime_->SetUserSettingsData(user_settings_data_);
|
||||
runtime_->SetSemanticsEnabled(semantics_enabled_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ class Engine : public blink::RuntimeDelegate {
|
||||
bool HandleNavigationPlatformMessage(
|
||||
fxl::RefPtr<blink::PlatformMessage> message);
|
||||
bool HandleLocalizationPlatformMessage(blink::PlatformMessage* message);
|
||||
bool HandleSystemPlatformMessage(blink::PlatformMessage* message);
|
||||
void HandleSettingsPlatformMessage(blink::PlatformMessage* message);
|
||||
|
||||
void HandleAssetPlatformMessage(fxl::RefPtr<blink::PlatformMessage> message);
|
||||
bool GetAssetAsBuffer(const std::string& name, std::vector<uint8_t>* data);
|
||||
@ -112,7 +112,7 @@ class Engine : public blink::RuntimeDelegate {
|
||||
blink::ViewportMetrics viewport_metrics_;
|
||||
std::string language_code_;
|
||||
std::string country_code_;
|
||||
double text_scale_factor_;
|
||||
std::string user_settings_data_;
|
||||
bool semantics_enabled_ = false;
|
||||
// TODO(abarth): Unify these two behind a common interface.
|
||||
fxl::RefPtr<blink::ZipAssetStore> asset_store_;
|
||||
|
||||
@ -17,6 +17,7 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Matrix;
|
||||
import android.os.Build;
|
||||
import android.text.format.DateFormat;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
@ -101,6 +102,7 @@ public class FlutterView extends SurfaceView
|
||||
private final BasicMessageChannel<Object> mFlutterKeyEventChannel;
|
||||
private final BasicMessageChannel<String> mFlutterLifecycleChannel;
|
||||
private final BasicMessageChannel<Object> mFlutterSystemChannel;
|
||||
private final BasicMessageChannel<Object> mFlutterSettingsChannel;
|
||||
private final BroadcastReceiver mDiscoveryReceiver;
|
||||
private final List<ActivityLifecycleListener> mActivityLifecycleListeners;
|
||||
private final List<FirstFrameListener> mFirstFrameListeners;
|
||||
@ -173,6 +175,8 @@ public class FlutterView extends SurfaceView
|
||||
StringCodec.INSTANCE);
|
||||
mFlutterSystemChannel = new BasicMessageChannel<>(this, "flutter/system",
|
||||
JSONMessageCodec.INSTANCE);
|
||||
mFlutterSettingsChannel = new BasicMessageChannel<>(this, "flutter/settings",
|
||||
JSONMessageCodec.INSTANCE);
|
||||
|
||||
// TODO(plugins): Change PlatformPlugin to accept a Context. Disable the
|
||||
// operations that require an Activity when a Context is passed.
|
||||
@ -186,7 +190,7 @@ public class FlutterView extends SurfaceView
|
||||
mTextInputPlugin = new TextInputPlugin(this);
|
||||
|
||||
setLocale(getResources().getConfiguration().locale);
|
||||
setTextScaleFactor(getResources().getConfiguration().fontScale);
|
||||
setUserSettings();
|
||||
|
||||
if ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
|
||||
mDiscoveryReceiver = new DiscoveryReceiver();
|
||||
@ -284,11 +288,11 @@ public class FlutterView extends SurfaceView
|
||||
mFlutterNavigationChannel.invokeMethod("popRoute", null);
|
||||
}
|
||||
|
||||
private void setTextScaleFactor(float textScaleFactor) {
|
||||
private void setUserSettings() {
|
||||
Map<String, Object> message = new HashMap<>();
|
||||
message.put("type", "systemSettings");
|
||||
message.put("textScaleFactor", textScaleFactor);
|
||||
mFlutterSystemChannel.send(message);
|
||||
message.put("textScaleFactor", getResources().getConfiguration().fontScale);
|
||||
message.put("alwaysUse24HourFormat", DateFormat.is24HourFormat(getContext()));
|
||||
mFlutterSettingsChannel.send(message);
|
||||
}
|
||||
|
||||
private void setLocale(Locale locale) {
|
||||
@ -300,7 +304,7 @@ public class FlutterView extends SurfaceView
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
setLocale(newConfig.locale);
|
||||
setTextScaleFactor(newConfig.fontScale);
|
||||
setUserSettings();
|
||||
}
|
||||
|
||||
float getDevicePixelRatio() {
|
||||
|
||||
@ -72,6 +72,7 @@ class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse {
|
||||
fml::scoped_nsprotocol<FlutterMethodChannel*> _textInputChannel;
|
||||
fml::scoped_nsprotocol<FlutterBasicMessageChannel*> _lifecycleChannel;
|
||||
fml::scoped_nsprotocol<FlutterBasicMessageChannel*> _systemChannel;
|
||||
fml::scoped_nsprotocol<FlutterBasicMessageChannel*> _settingsChannel;
|
||||
fml::scoped_nsprotocol<UIView*> _launchView;
|
||||
bool _platformSupportsTouchTypes;
|
||||
bool _platformSupportsTouchPressure;
|
||||
@ -177,6 +178,11 @@ class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse {
|
||||
binaryMessenger:self
|
||||
codec:[FlutterJSONMessageCodec sharedInstance]]);
|
||||
|
||||
_settingsChannel.reset([[FlutterBasicMessageChannel alloc]
|
||||
initWithName:@"flutter/settings"
|
||||
binaryMessenger:self
|
||||
codec:[FlutterJSONMessageCodec sharedInstance]]);
|
||||
|
||||
_platformPlugin.reset([[FlutterPlatformPlugin alloc] init]);
|
||||
[_platformChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
|
||||
[_platformPlugin.get() handleMethodCall:call result:result];
|
||||
@ -247,6 +253,11 @@ class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse {
|
||||
selector:@selector(onMemoryWarning:)
|
||||
name:UIApplicationDidReceiveMemoryWarningNotification
|
||||
object:nil];
|
||||
|
||||
[center addObserver:self
|
||||
selector:@selector(onUserSettingsChanged:)
|
||||
name:UIContentSizeCategoryDidChangeNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)setInitialRoute:(NSString*)route {
|
||||
@ -340,6 +351,7 @@ class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse {
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
TRACE_EVENT0("flutter", "viewDidAppear");
|
||||
[self onLocaleUpdated:nil];
|
||||
[self onUserSettingsChanged:nil];
|
||||
[self onVoiceOverChanged:nil];
|
||||
[_lifecycleChannel.get() sendMessage:@"AppLifecycleState.resumed"];
|
||||
|
||||
@ -693,6 +705,60 @@ static inline blink::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* to
|
||||
[_localizationChannel.get() invokeMethod:@"setLocale" arguments:@[ languageCode, countryCode ]];
|
||||
}
|
||||
|
||||
#pragma mark - Set user settings
|
||||
|
||||
- (void)onUserSettingsChanged:(NSNotification*)notification {
|
||||
[_settingsChannel.get() sendMessage:@{
|
||||
@"textScaleFactor" : @([self textScaleFactor]),
|
||||
@"alwaysUse24HourFormat" : @([self isAlwaysUse24HourFormat]),
|
||||
}];
|
||||
}
|
||||
|
||||
- (CGFloat)textScaleFactor {
|
||||
UIContentSizeCategory category = [UIApplication sharedApplication].preferredContentSizeCategory;
|
||||
// The delta is computed based on the following:
|
||||
// - L (large) is the default 1.0 scale.
|
||||
// - The scale is linear spanning from XS to XXXL.
|
||||
// - XXXL = 1.4 * XS.
|
||||
//
|
||||
// L = 1.0 = XS + 3 * delta
|
||||
// XXXL = 1.4 * XS = XS + 6 * delta
|
||||
const CGFloat delta = 0.055555;
|
||||
if ([category isEqualToString:UIContentSizeCategoryExtraSmall])
|
||||
return 1.0 - 3 * delta;
|
||||
else if ([category isEqualToString:UIContentSizeCategorySmall])
|
||||
return 1.0 - 2 * delta;
|
||||
else if ([category isEqualToString:UIContentSizeCategoryMedium])
|
||||
return 1.0 - delta;
|
||||
else if ([category isEqualToString:UIContentSizeCategoryLarge])
|
||||
return 1.0;
|
||||
else if ([category isEqualToString:UIContentSizeCategoryExtraLarge])
|
||||
return 1.0 + delta;
|
||||
else if ([category isEqualToString:UIContentSizeCategoryExtraExtraLarge])
|
||||
return 1.0 + 2 * delta;
|
||||
else if ([category isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge])
|
||||
return 1.0 + 3 * delta;
|
||||
else
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
#pragma mark - Status Bar touch event handling
|
||||
|
||||
// Standard iOS status bar height in pixels.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user