Skip to content

Commit

Permalink
Add alwaysUse24HourFormat and textScaleFactor (flutter#4202)
Browse files Browse the repository at this point in the history
* systems/settings channel split

* merge textScaleFactor and alwaysUse24HourFormat into flutter/settings channel

* add debugOverrideAlwaysUse24HourFormat

* implement textScaleFactor on iOS

* address comments

* remove debugOverrideAlwaysUse24HourFormat

* clang-format
  • Loading branch information
yjbanov authored Oct 18, 2017
1 parent d3ebce9 commit 23f5ccd
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 50 deletions.
10 changes: 10 additions & 0 deletions lib/ui/hooks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 7 additions & 0 deletions lib/ui/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions lib/ui/window/window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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),
});
}

Expand Down
2 changes: 1 addition & 1 deletion lib/ui/window/window.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 4 additions & 4 deletions runtime/runtime_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions runtime/runtime_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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_;

Expand Down
42 changes: 10 additions & 32 deletions shell/common/engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {}
Expand Down Expand Up @@ -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_) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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_);
}
}
Expand Down
4 changes: 2 additions & 2 deletions shell/common/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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_;
Expand Down
16 changes: 10 additions & 6 deletions shell/platform/android/io/flutter/view/FlutterView.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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;
Expand Down Expand Up @@ -101,6 +102,7 @@ static final class ViewportMetrics {
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;
Expand Down Expand Up @@ -173,6 +175,8 @@ public void surfaceDestroyed(SurfaceHolder holder) {
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.
Expand All @@ -186,7 +190,7 @@ public void surfaceDestroyed(SurfaceHolder holder) {
mTextInputPlugin = new TextInputPlugin(this);

setLocale(getResources().getConfiguration().locale);
setTextScaleFactor(getResources().getConfiguration().fontScale);
setUserSettings();

if ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
mDiscoveryReceiver = new DiscoveryReceiver();
Expand Down Expand Up @@ -284,11 +288,11 @@ public void popRoute() {
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) {
Expand All @@ -300,7 +304,7 @@ private void setLocale(Locale locale) {
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setLocale(newConfig.locale);
setTextScaleFactor(newConfig.fontScale);
setUserSettings();
}

float getDevicePixelRatio() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ @implementation FlutterViewController {
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;
Expand Down Expand Up @@ -177,6 +178,11 @@ - (void)performCommonViewControllerInitialization {
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];
Expand Down Expand Up @@ -247,6 +253,11 @@ - (void)setupNotificationCenterObservers {
selector:@selector(onMemoryWarning:)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];

[center addObserver:self
selector:@selector(onUserSettingsChanged:)
name:UIContentSizeCategoryDidChangeNotification
object:nil];
}

- (void)setInitialRoute:(NSString*)route {
Expand Down Expand Up @@ -340,6 +351,7 @@ - (void)viewWillAppear:(BOOL)animated {
- (void)viewDidAppear:(BOOL)animated {
TRACE_EVENT0("flutter", "viewDidAppear");
[self onLocaleUpdated:nil];
[self onUserSettingsChanged:nil];
[self onVoiceOverChanged:nil];
[_lifecycleChannel.get() sendMessage:@"AppLifecycleState.resumed"];

Expand Down Expand Up @@ -693,6 +705,60 @@ - (void)onLocaleUpdated:(NSNotification*)notification {
[_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.
Expand Down

0 comments on commit 23f5ccd

Please sign in to comment.