[settings] Reland: Add settings package.
This is a reland of 6ad59b980460281f26b8f44a99677e8b894322a9
Original change's description:
> [settings] Add settings package.
>
> Allows changing wifi and datetime settings.
> - Add a default proposer to launch settings from Ask bar.
> - Add timezone watcher to update timezone when it changes.
> - Add datetime_settings, display_settings and settings to
> ermine product dependencies.
>
> Change-Id: I664c4575a2a4506ff211ac9f33b5c246d6965997
Change-Id: If514f4610e02a98d9f59d5d694ca749aec6746fa
diff --git a/bin/datetime_settings/BUILD.gn b/bin/datetime_settings/BUILD.gn
new file mode 100644
index 0000000..90fa71f
--- /dev/null
+++ b/bin/datetime_settings/BUILD.gn
@@ -0,0 +1,31 @@
+# Copyright 2017 The Fuchsia 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("//topaz/runtime/flutter_runner/flutter_app.gni")
+
+flutter_app("datetime_settings") {
+
+ main_dart = "lib/main.dart"
+
+ manifest = "pubspec.yaml"
+
+ sources = [
+ ]
+
+ meta = [
+ {
+ path = rebase_path("meta/datetime_settings.cmx")
+ dest = "datetime_settings.cmx"
+ },
+ ]
+
+ deps = [
+ "//sdk/fidl/fuchsia.timezone",
+ "//third_party/dart-pkg/git/flutter/packages/flutter",
+ "//topaz/lib/settings:lib.settings",
+ "//topaz/public/dart/fuchsia_logger",
+ "//topaz/public/dart/fuchsia_services",
+ "//topaz/public/dart/widgets:lib.widgets"
+ ]
+}
diff --git a/bin/datetime_settings/OWNERS b/bin/datetime_settings/OWNERS
new file mode 100644
index 0000000..ce2ec43
--- /dev/null
+++ b/bin/datetime_settings/OWNERS
@@ -0,0 +1,3 @@
+ejia@google.com
+brycelee@google.com
+sanjayc@google.com
\ No newline at end of file
diff --git a/bin/datetime_settings/analysis_options.yaml b/bin/datetime_settings/analysis_options.yaml
new file mode 100644
index 0000000..e688f26
--- /dev/null
+++ b/bin/datetime_settings/analysis_options.yaml
@@ -0,0 +1,5 @@
+# Copyright 2017 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+include: ../analysis_options.yaml
diff --git a/bin/datetime_settings/assets/ic_signal_wifi_0_bar_grey600_48dp.png b/bin/datetime_settings/assets/ic_signal_wifi_0_bar_grey600_48dp.png
new file mode 100644
index 0000000..4e59c6a
--- /dev/null
+++ b/bin/datetime_settings/assets/ic_signal_wifi_0_bar_grey600_48dp.png
Binary files differ
diff --git a/bin/datetime_settings/assets/ic_signal_wifi_1_bar_grey600_48dp.png b/bin/datetime_settings/assets/ic_signal_wifi_1_bar_grey600_48dp.png
new file mode 100644
index 0000000..36c2cca
--- /dev/null
+++ b/bin/datetime_settings/assets/ic_signal_wifi_1_bar_grey600_48dp.png
Binary files differ
diff --git a/bin/datetime_settings/assets/ic_signal_wifi_1_bar_lock_grey600_48dp.png b/bin/datetime_settings/assets/ic_signal_wifi_1_bar_lock_grey600_48dp.png
new file mode 100644
index 0000000..35e3881
--- /dev/null
+++ b/bin/datetime_settings/assets/ic_signal_wifi_1_bar_lock_grey600_48dp.png
Binary files differ
diff --git a/bin/datetime_settings/assets/ic_signal_wifi_2_bar_grey600_48dp.png b/bin/datetime_settings/assets/ic_signal_wifi_2_bar_grey600_48dp.png
new file mode 100644
index 0000000..9cf549d
--- /dev/null
+++ b/bin/datetime_settings/assets/ic_signal_wifi_2_bar_grey600_48dp.png
Binary files differ
diff --git a/bin/datetime_settings/assets/ic_signal_wifi_2_bar_lock_grey600_48dp.png b/bin/datetime_settings/assets/ic_signal_wifi_2_bar_lock_grey600_48dp.png
new file mode 100644
index 0000000..8b5f3e6
--- /dev/null
+++ b/bin/datetime_settings/assets/ic_signal_wifi_2_bar_lock_grey600_48dp.png
Binary files differ
diff --git a/bin/datetime_settings/assets/ic_signal_wifi_3_bar_grey600_48dp.png b/bin/datetime_settings/assets/ic_signal_wifi_3_bar_grey600_48dp.png
new file mode 100644
index 0000000..eb55802
--- /dev/null
+++ b/bin/datetime_settings/assets/ic_signal_wifi_3_bar_grey600_48dp.png
Binary files differ
diff --git a/bin/datetime_settings/assets/ic_signal_wifi_3_bar_lock_grey600_48dp.png b/bin/datetime_settings/assets/ic_signal_wifi_3_bar_lock_grey600_48dp.png
new file mode 100644
index 0000000..2f37f9a
--- /dev/null
+++ b/bin/datetime_settings/assets/ic_signal_wifi_3_bar_lock_grey600_48dp.png
Binary files differ
diff --git a/bin/datetime_settings/assets/ic_signal_wifi_4_bar_grey600_48dp.png b/bin/datetime_settings/assets/ic_signal_wifi_4_bar_grey600_48dp.png
new file mode 100644
index 0000000..3941290
--- /dev/null
+++ b/bin/datetime_settings/assets/ic_signal_wifi_4_bar_grey600_48dp.png
Binary files differ
diff --git a/bin/datetime_settings/assets/ic_signal_wifi_4_bar_lock_grey600_48dp.png b/bin/datetime_settings/assets/ic_signal_wifi_4_bar_lock_grey600_48dp.png
new file mode 100644
index 0000000..63661cc
--- /dev/null
+++ b/bin/datetime_settings/assets/ic_signal_wifi_4_bar_lock_grey600_48dp.png
Binary files differ
diff --git a/bin/datetime_settings/lib/main.dart b/bin/datetime_settings/lib/main.dart
new file mode 100644
index 0000000..3296e45
--- /dev/null
+++ b/bin/datetime_settings/lib/main.dart
@@ -0,0 +1,40 @@
+// Copyright 2019 The Fuchsia 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 'package:flutter/material.dart';
+
+import 'package:fidl_fuchsia_timezone/fidl_async.dart' show TimezoneProxy;
+import 'package:fuchsia_logger/logger.dart' show setupLogger;
+import 'package:fuchsia_services/services.dart' show StartupContext;
+import 'package:lib.settings/timezone_picker.dart';
+
+/// Main entry point to the datetime settings module.
+Future<void> main() async {
+ setupLogger(name: 'datetime_settings');
+
+ final timezone = TimezoneProxy();
+ StartupContext.fromStartupInfo().incoming.connectToService(timezone);
+ final currentTimezone = ValueNotifier<String>(await timezone.getTimezoneId());
+
+ Widget app = MaterialApp(
+ home: Container(
+ child: Container(
+ color: Colors.white,
+ child: AnimatedBuilder(
+ animation: currentTimezone,
+ builder: (context, child) {
+ return TimezonePicker(
+ onTap: (tz) {
+ timezone.setTimezone(tz);
+ currentTimezone.value = tz;
+ },
+ currentTimezoneId: currentTimezone.value,
+ );
+ }),
+ ),
+ ),
+ );
+
+ runApp(app);
+}
diff --git a/bin/datetime_settings/meta/datetime_settings.cmx b/bin/datetime_settings/meta/datetime_settings.cmx
new file mode 100644
index 0000000..cced215
--- /dev/null
+++ b/bin/datetime_settings/meta/datetime_settings.cmx
@@ -0,0 +1,37 @@
+{
+ "program": {
+ "data": "data/datetime_settings"
+ },
+ "sandbox": {
+ "services": [
+ "fuchsia.fonts.Provider",
+ "fuchsia.modular.Clipboard",
+ "fuchsia.modular.ContextWriter",
+ "fuchsia.logger.LogSink",
+ "fuchsia.sys.Environment",
+ "fuchsia.timezone.Timezone",
+ "fuchsia.ui.input.ImeService",
+ "fuchsia.ui.policy.Presenter",
+ "fuchsia.ui.scenic.Scenic",
+ "fuchsia.wlan.service.Wlan"
+ ],
+ "system": [ "data" ]
+ },
+ "facets": {
+ "fuchsia.module": {
+ "@version": 2,
+ "suggestion_headline": "Date-Time Settings",
+ "intent_filters": [
+ {
+ "action": "com.fuchsia.settings.datetime.show",
+ "parameters": [
+ {
+ "name": "status",
+ "type": "com.fuchsia.status"
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/bin/datetime_settings/pubspec.yaml b/bin/datetime_settings/pubspec.yaml
new file mode 100644
index 0000000..8b2b91e
--- /dev/null
+++ b/bin/datetime_settings/pubspec.yaml
@@ -0,0 +1,16 @@
+# Copyright 2017 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+name: wifi_settings
+flutter:
+ assets:
+ - assets/ic_signal_wifi_0_bar_grey600_48dp.png
+ - assets/ic_signal_wifi_1_bar_lock_grey600_48dp.png
+ - assets/ic_signal_wifi_1_bar_grey600_48dp.png
+ - assets/ic_signal_wifi_2_bar_lock_grey600_48dp.png
+ - assets/ic_signal_wifi_2_bar_grey600_48dp.png
+ - assets/ic_signal_wifi_3_bar_lock_grey600_48dp.png
+ - assets/ic_signal_wifi_3_bar_grey600_48dp.png
+ - assets/ic_signal_wifi_4_bar_lock_grey600_48dp.png
+ - assets/ic_signal_wifi_4_bar_grey600_48dp.png
\ No newline at end of file
diff --git a/bin/settings/BUILD.gn b/bin/settings/BUILD.gn
new file mode 100644
index 0000000..7ffd535
--- /dev/null
+++ b/bin/settings/BUILD.gn
@@ -0,0 +1,63 @@
+# Copyright 2018 The Fuchsia 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("//build/package.gni")
+import("//topaz/runtime/dart/flutter_test.gni")
+import("//topaz/runtime/flutter_runner/flutter_app.gni")
+
+flutter_app("settings") {
+
+ main_dart = "lib/main.dart"
+
+ package_name = "settings"
+
+ manifest = "pubspec.yaml"
+
+ meta = [
+ {
+ path = rebase_path("meta/settings.cmx")
+ dest = "settings.cmx"
+ },
+ ]
+
+ sources = [
+ "src/models/settings_model.dart",
+ "src/widgets/all_settings.dart",
+ ]
+
+ deps = [
+ "//sdk/fidl/fuchsia.modular",
+ "//sdk/fidl/fuchsia.timezone",
+ "//sdk/fidl/fuchsia.ui.views",
+ "//sdk/fidl/fuchsia.wlan.service",
+ "//third_party/dart-pkg/git/flutter/packages/flutter_test",
+ "//third_party/dart-pkg/git/flutter/packages/flutter",
+ "//third_party/dart-pkg/pub/meta",
+ "//topaz/lib/settings:lib.settings",
+ "//topaz/public/dart/fuchsia_logger",
+ "//topaz/public/dart/fuchsia_modular",
+ "//topaz/public/dart/fuchsia_scenic_flutter",
+ "//topaz/public/dart/fuchsia_services",
+ "//topaz/public/dart/widgets:lib.widgets",
+ ]
+}
+
+flutter_test("settings_tests") {
+ sources = [
+ "settings_model_test.dart",
+ ]
+
+ deps = [
+ ":settings_dart_library",
+ "//third_party/dart-pkg/pub/test",
+ ]
+}
+
+group("tests") {
+ testonly = true
+
+ deps = [
+ ":settings_tests",
+ ]
+}
diff --git a/bin/settings/README.md b/bin/settings/README.md
new file mode 100644
index 0000000..1970c2e
--- /dev/null
+++ b/bin/settings/README.md
@@ -0,0 +1,6 @@
+# settings
+
+Used by the Session Shell as the settings story.
+
+This app will serve as the settings story for Session Shells.
+
diff --git a/bin/settings/analysis_options.yaml b/bin/settings/analysis_options.yaml
new file mode 100644
index 0000000..9d84659
--- /dev/null
+++ b/bin/settings/analysis_options.yaml
@@ -0,0 +1,5 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+include: ../../tools/analysis_options.yaml
diff --git a/bin/settings/lib/main.dart b/bin/settings/lib/main.dart
new file mode 100644
index 0000000..0a2c34d
--- /dev/null
+++ b/bin/settings/lib/main.dart
@@ -0,0 +1,77 @@
+// Copyright 2018 The Fuchsia 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:async';
+import 'dart:developer' show Timeline;
+
+import 'package:flutter/material.dart';
+import 'package:fuchsia_logger/logger.dart';
+
+import 'src/models/settings_model.dart';
+import 'src/widgets/all_settings.dart';
+
+/// Main function of settings.
+Future<Null> main() async {
+ setupLogger(name: 'settings');
+ Timeline.instantSync('settings starting');
+
+ SettingsModel settingsModel = SettingsModel();
+
+ Widget app = MaterialApp(
+ home: AllSettings(),
+ routes: <String, WidgetBuilder>{
+ '/wifi': (BuildContext context) => _buildModule(
+ 'Wi-Fi',
+ () => settingsModel.wifiModule,
+ ),
+ '/datetime': (BuildContext context) => _buildModule(
+ 'Date & Time',
+ () => settingsModel.datetimeModule,
+ ),
+ '/display': (BuildContext context) => _buildModule(
+ 'Display',
+ () => settingsModel.displayModule,
+ ),
+ '/accessibility': (BuildContext context) => _buildModule(
+ 'Accessibility',
+ () => settingsModel.accessibilitySettingsModule,
+ ),
+ '/experiments': (BuildContext context) => _buildModule(
+ 'Experiments',
+ () => settingsModel.experimentsModule,
+ ),
+ '/system': (BuildContext context) => _buildModule(
+ 'System',
+ () => settingsModel.deviceSettingsModule,
+ ),
+ '/licenses': (BuildContext context) => LicensePage()
+ },
+ );
+
+ app = ScopedModel<SettingsModel>(
+ model: settingsModel,
+ child: app,
+ );
+
+ runApp(app);
+
+ Timeline.instantSync('settings started');
+}
+
+// Returns the [Scaffold] widget for the root view of the module.
+Widget _buildModule(String title, Widget getModView()) {
+ return ScopedModelDescendant<SettingsModel>(
+ builder: (
+ BuildContext context,
+ Widget child,
+ SettingsModel settingsModel,
+ ) =>
+ Scaffold(
+ appBar: AppBar(
+ title: Text(title),
+ ),
+ body: getModView(),
+ ),
+ );
+}
diff --git a/bin/settings/lib/src/models/settings_model.dart b/bin/settings/lib/src/models/settings_model.dart
new file mode 100644
index 0000000..3b453fc
--- /dev/null
+++ b/bin/settings/lib/src/models/settings_model.dart
@@ -0,0 +1,222 @@
+// Copyright 2018 The Fuchsia 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:async';
+import 'dart:io';
+
+import 'package:intl/intl.dart';
+import 'package:fidl_fuchsia_ui_views/fidl_async.dart';
+import 'package:fuchsia_logger/logger.dart';
+import 'package:lib.settings/device_info.dart';
+import 'package:lib.widgets/model.dart';
+import 'package:fuchsia_modular/module.dart';
+import 'package:fuchsia_scenic_flutter/child_view.dart' show ChildView;
+import 'package:fuchsia_scenic_flutter/child_view_connection.dart';
+
+import 'settings_status.dart';
+
+export 'package:lib.widgets/model.dart'
+ show ScopedModel, Model, ScopedModelDescendant;
+
+/// Model for settings view.
+class SettingsModel extends Model {
+ String _networkAddresses;
+
+ SettingsStatus _settingsStatus;
+
+ // Wi-Fi module.
+ _CachedModule _wifiModule;
+
+ // Datetime module.
+ _CachedModule _datetimeModule;
+
+ // Display module.
+ _CachedModule _displayModule;
+
+ // Accessibility module.
+ _CachedModule _accessibilityModule;
+
+ // Experiments module.
+ _CachedModule _experimentsModule;
+
+ /// Module for general device settings, including update.
+ _CachedModule _deviceSettingsModule;
+
+ DateTime _testDeviceSourceDate;
+
+ /// Constructor.
+ SettingsModel() {
+ initialize();
+ }
+
+ // Exposed for testing.
+ void initialize() {
+ // Explicitly ignore intents
+ Module().registerIntentHandler(NoopIntentHandler());
+ _settingsStatus = SettingsStatus()..addListener(notifyListeners);
+ _setNetworkAddresses();
+ }
+
+ /// Fetches and sets the addresses of all network interfaces delimited by space.
+ Future<void> _setNetworkAddresses() async {
+ var interfaces = await NetworkInterface.list();
+ _networkAddresses = interfaces
+ .expand((NetworkInterface interface) => interface.addresses)
+ .map((InternetAddress address) => address.address)
+ .join(' ');
+ notifyListeners();
+ }
+
+ /// Returns the addresses of all network interfaces delimited by space.
+ String get networkAddresses {
+ return _networkAddresses;
+ }
+
+ /// Returns the hostname of the running device.
+ String get hostname => Platform.localHostname;
+
+ /// Returns the build info, if build info file is found on the system image.
+ String get buildInfo {
+ final DateTime buildTimeStamp = _testDeviceSourceDate != null
+ ? _testDeviceSourceDate
+ : DeviceInfo.getSourceDate();
+
+ if (buildTimeStamp != null) {
+ final builtAt =
+ DateFormat('H:mm', 'en_US').format(buildTimeStamp).toLowerCase();
+ final builtOn =
+ DateFormat('MMM dd, yyyy', 'en_US').format(buildTimeStamp);
+ // The time zone is hardcoded because DateFormat and DateTime currently doesn't
+ // support time zones.
+ return 'Built at $builtAt UTC on $builtOn';
+ } else {
+ log.warning('Last built time doesn\'t exist!');
+ }
+ return null;
+ }
+
+ /// Returns the wifi status.
+ String get wifiStatus => _settingsStatus.wifiStatus;
+
+ /// Returns the [EmbeddedModule] for Wi-Fi.
+ ChildView get wifiModule {
+ if (_wifiModule == null) {
+ _embedSetting(
+ name: 'wifi_settings',
+ title: 'Wi-Fi',
+ ).then((_CachedModule module) {
+ _wifiModule = module;
+ notifyListeners();
+ });
+ }
+ return _wifiModule?.childView;
+ }
+
+ /// Returns the datetime status.
+ String get datetimeStatus => _settingsStatus.timezoneStatus;
+
+ /// Returns the [EmbeddedModule] for Date & Time.
+ ChildView get datetimeModule {
+ if (_datetimeModule == null) {
+ _embedSetting(
+ name: 'datetime_settings',
+ title: 'Date & Time',
+ ).then((_CachedModule module) {
+ _datetimeModule = module;
+ notifyListeners();
+ });
+ }
+ return _datetimeModule?.childView;
+ }
+
+ /// Returns the [EmbeddedModule] for Experiments.
+ ChildView get experimentsModule {
+ if (_experimentsModule == null) {
+ _embedSetting(
+ name: 'experiments_setting',
+ title: 'Experiments',
+ ).then((_CachedModule module) {
+ _experimentsModule = module;
+ notifyListeners();
+ });
+ }
+ return _experimentsModule?.childView;
+ }
+
+ /// Returns the [EmbeddedModule] for Display.
+ ChildView get displayModule {
+ if (_displayModule == null) {
+ _embedSetting(
+ name: 'display_settings',
+ title: 'Display',
+ ).then((_CachedModule module) {
+ _displayModule = module;
+ notifyListeners();
+ });
+ }
+ return _displayModule?.childView;
+ }
+
+ /// Returns the [EmbeddedModule] for accessibility settings.
+ ChildView get accessibilitySettingsModule {
+ if (_accessibilityModule == null) {
+ _embedSetting(
+ name: 'accessibility_settings',
+ title: 'Accessibility',
+ ).then((_CachedModule module) {
+ _accessibilityModule = module;
+ notifyListeners();
+ });
+ }
+ return _accessibilityModule?.childView;
+ }
+
+ /// Returns the [EmbeddedModule] for device settings.
+ ChildView get deviceSettingsModule {
+ if (_deviceSettingsModule == null) {
+ _embedSetting(
+ name: 'device_settings',
+ title: 'System',
+ ).then((_CachedModule module) {
+ _deviceSettingsModule = module;
+ notifyListeners();
+ });
+ }
+ return _deviceSettingsModule?.childView;
+ }
+
+ Future<_CachedModule> _embedSetting({
+ String name,
+ String title,
+ }) {
+ final intent = Intent(
+ action: '',
+ handler: 'fuchsia-pkg://fuchsia.com/$name#meta/$name.cmx',
+ );
+ return Module()
+ .embedModule(name: title, intent: intent)
+ .then((m) => _CachedModule(m));
+ }
+
+ set testDeviceSourceDate(DateTime testDeviceSourceDate) =>
+ _testDeviceSourceDate = testDeviceSourceDate;
+}
+
+/// A helper class which holds a reference to the [EmbeddedModule]
+/// and creates the [childView] on demand.
+class _CachedModule {
+ final EmbeddedModule _embeddedModule;
+ ChildView _childView;
+
+ /// Returns an instance of the [ChildView] for this module.
+ ChildView get childView {
+ return _childView ??= _makeChildView();
+ }
+
+ _CachedModule(this._embeddedModule);
+
+ ChildView _makeChildView() => ChildView(
+ connection: ChildViewConnection(
+ ViewHolderToken(value: _embeddedModule.viewHolderToken.value)));
+}
diff --git a/bin/settings/lib/src/models/settings_status.dart b/bin/settings/lib/src/models/settings_status.dart
new file mode 100644
index 0000000..7bfbc49
--- /dev/null
+++ b/bin/settings/lib/src/models/settings_status.dart
@@ -0,0 +1,96 @@
+// Copyright 2018 The Fuchsia 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:async';
+
+import 'package:fidl_fuchsia_timezone/fidl_async.dart' as tz;
+import 'package:fidl_fuchsia_wlan_service/fidl_async.dart' as wlan;
+import 'package:flutter/foundation.dart';
+import 'package:fuchsia_services/services.dart';
+
+// Interval for scanning in seconds.
+const _statusUpdateInterval = 6;
+
+/// Class that listens to and sends out status shown in the all_settings view.
+class SettingsStatus extends ChangeNotifier {
+ final ValueNotifier<String> _wifiStatus = ValueNotifier<String>('');
+ final ValueNotifier<String> _timezoneStatus = ValueNotifier<String>('');
+
+ final wlan.WlanProxy _wlanProxy = wlan.WlanProxy();
+ final tz.TimezoneProxy _timezoneProxy = tz.TimezoneProxy();
+ final tz.TimezoneWatcherBinding _timezoneWatcherBinding =
+ tz.TimezoneWatcherBinding();
+
+ Timer _wlanUpdateTimer;
+ SettingsStatus() {
+ _wifiStatus.addListener(notifyListeners);
+ _timezoneStatus.addListener(notifyListeners);
+
+ _listenToWifiStatus();
+ _listenToTimezoneStatus();
+ }
+
+ String get timezoneStatus => _timezoneStatus.value;
+
+ String get wifiStatus => _wifiStatus.value;
+
+ /// Stop any updating timers
+ void stop() {
+ _wlanUpdateTimer.cancel();
+ }
+
+ String _extractTimezoneStatus(String timezoneId) => '$timezoneId';
+
+ String _extractWifiStatus(wlan.WlanStatus status) {
+ switch (status.state) {
+ case wlan.State.associated:
+ return 'Connected to ${status.currentAp.ssid}';
+ case wlan.State.associating:
+ case wlan.State.joining:
+ case wlan.State.bss:
+ case wlan.State.querying:
+ case wlan.State.authenticating:
+ return 'Connecting';
+ case wlan.State.scanning:
+ return 'Disconnected';
+ default:
+ return 'Unknown';
+ }
+ }
+
+ void _listenToTimezoneStatus() async {
+ StartupContext.fromStartupInfo().incoming.connectToService(_timezoneProxy);
+
+ _timezoneStatus.value =
+ _extractTimezoneStatus(await _timezoneProxy.getTimezoneId());
+
+ await _timezoneProxy
+ .watch(_timezoneWatcherBinding.wrap(_TimezoneWatcherImpl(this)));
+ }
+
+ void _listenToWifiStatus() async {
+ StartupContext.fromStartupInfo().incoming.connectToService(_wlanProxy);
+
+ _wifiStatus.value = _extractWifiStatus(await _wlanProxy.status());
+
+ _wlanUpdateTimer =
+ Timer.periodic(Duration(seconds: _statusUpdateInterval), (_) async {
+ _wifiStatus.value = _extractWifiStatus(await _wlanProxy.status());
+ });
+ }
+
+ void _onChangeTimezone(String tz) {
+ _timezoneStatus.value = _extractTimezoneStatus(tz);
+ }
+}
+
+class _TimezoneWatcherImpl extends tz.TimezoneWatcher {
+ final SettingsStatus status;
+ _TimezoneWatcherImpl(this.status);
+
+ @override
+ Future<void> onTimezoneOffsetChange(String timezoneId) async {
+ status._onChangeTimezone(timezoneId);
+ }
+}
diff --git a/bin/settings/lib/src/widgets/all_settings.dart b/bin/settings/lib/src/widgets/all_settings.dart
new file mode 100644
index 0000000..654637c
--- /dev/null
+++ b/bin/settings/lib/src/widgets/all_settings.dart
@@ -0,0 +1,75 @@
+// Copyright 2018 The Fuchsia 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 'package:flutter/material.dart';
+import 'package:lib.widgets/model.dart';
+
+import '../models/settings_model.dart';
+
+/// Main view that shows all settings.
+class AllSettings extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return ScopedModelDescendant<SettingsModel>(
+ builder: (
+ BuildContext context,
+ Widget child,
+ SettingsModel settingsModel,
+ ) =>
+ Scaffold(
+ appBar: AppBar(
+ title: Text('All Settings'),
+ ),
+ body: ListView(
+ physics: BouncingScrollPhysics(),
+ children: <Widget>[
+ ListTile(
+ leading: Icon(Icons.wifi),
+ title: Text('Wi-Fi'),
+ subtitle: Text(settingsModel.wifiStatus),
+ onTap: () => Navigator.of(context).pushNamed('/wifi'),
+ ),
+ ListTile(
+ leading: Icon(Icons.schedule),
+ title: Text('Date & time'),
+ subtitle: Text(settingsModel.datetimeStatus),
+ onTap: () => Navigator.of(context).pushNamed('/datetime'),
+ ),
+ ListTile(
+ leading: Icon(Icons.settings_brightness),
+ title: Text('Display'),
+ onTap: () => Navigator.of(context).pushNamed('/display'),
+ ),
+ ListTile(
+ leading: Icon(Icons.accessibility),
+ title: Text('Accessibility'),
+ onTap: () =>
+ Navigator.of(context).pushNamed('/accessibility'),
+ ),
+ ListTile(
+ leading: Icon(Icons.touch_app),
+ title: Text('Experiments'),
+ onTap: () => Navigator.of(context).pushNamed('/experiments'),
+ ),
+ ListTile(
+ leading: Icon(Icons.info_outline),
+ title: Text('System'),
+ subtitle: Text(
+ '${settingsModel.hostname} '
+ '${settingsModel.networkAddresses} '
+ '${settingsModel.buildInfo}',
+ ),
+ onTap: () => Navigator.of(context).pushNamed('/system'),
+ ),
+ ListTile(
+ leading: Icon(Icons.copyright),
+ title: Text('Show Open Source Licenses'),
+ onTap: () => Navigator.of(context).pushNamed('/licenses'),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/bin/settings/meta/settings.cmx b/bin/settings/meta/settings.cmx
new file mode 100644
index 0000000..4c87f6a
--- /dev/null
+++ b/bin/settings/meta/settings.cmx
@@ -0,0 +1,23 @@
+{
+ "program": {
+ "data": "data/settings"
+ },
+ "sandbox": {
+ "features": [ "build-info" ],
+ "services": [
+ "fuchsia.cobalt.LoggerFactory",
+ "fuchsia.fonts.Provider",
+ "fuchsia.logger.LogSink",
+ "fuchsia.modular.Clipboard",
+ "fuchsia.modular.ContextWriter",
+ "fuchsia.modular.ModuleContext",
+ "fuchsia.sys.Environment",
+ "fuchsia.timezone.Timezone",
+ "fuchsia.ui.input.ImeService",
+ "fuchsia.ui.policy.Presenter",
+ "fuchsia.ui.scenic.Scenic",
+ "fuchsia.wlan.service.Wlan"
+ ],
+ "system": [ "data" ]
+ }
+}
diff --git a/bin/settings/pubspec.yaml b/bin/settings/pubspec.yaml
new file mode 100644
index 0000000..2fa4436
--- /dev/null
+++ b/bin/settings/pubspec.yaml
@@ -0,0 +1,4 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+name: settings
diff --git a/bin/settings/test/settings_model_test.dart b/bin/settings/test/settings_model_test.dart
new file mode 100644
index 0000000..ea21b45
--- /dev/null
+++ b/bin/settings/test/settings_model_test.dart
@@ -0,0 +1,22 @@
+// Copyright 2018 The Fuchsia 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 'package:settings/src/models/settings_model.dart'; // ignore: implementation_imports
+import 'package:test/test.dart';
+
+class TestSettingsModel extends SettingsModel {
+ @override
+ void initialize() {}
+}
+
+void main() {
+ TestSettingsModel _settingsModel = TestSettingsModel();
+
+ test('test buildInfo', () {
+ final DateTime testDate = DateTime.utc(2006, 10, 6, 13, 20, 0);
+ _settingsModel.testDeviceSourceDate = testDate;
+ expect(
+ _settingsModel.buildInfo, equals('Built at 13:20 UTC on Oct 06, 2006'));
+ });
+}
diff --git a/packages/prod/BUILD.gn b/packages/prod/BUILD.gn
index 0eb5474..13e3392 100644
--- a/packages/prod/BUILD.gn
+++ b/packages/prod/BUILD.gn
@@ -54,6 +54,7 @@
"//topaz/packages/prod:dart_jit_product_runner",
"//topaz/packages/prod:dart_jit_runner",
"//topaz/packages/prod:dart_runner",
+ "//topaz/packages/prod:datetime_settings",
"//topaz/packages/prod:device_settings",
"//topaz/packages/prod:display_settings",
"//topaz/packages/prod:ermine",
@@ -66,6 +67,7 @@
"//topaz/packages/prod:latin-ime",
"//topaz/packages/prod:modules_index",
"//topaz/packages/prod:mondrian",
+ "//topaz/packages/prod:settings",
"//topaz/packages/prod:skottie_viewer",
"//topaz/packages/prod:system_dashboard",
"//topaz/packages/prod:term",
@@ -91,6 +93,20 @@
]
}
+group("datetime_settings") {
+ testonly = true
+ public_deps = [
+ "//topaz/bin/datetime_settings",
+ ]
+}
+
+group("settings") {
+ testonly = true
+ public_deps = [
+ "//topaz/bin/settings",
+ ]
+}
+
group("wifi_settings") {
testonly = true
public_deps = [
@@ -253,8 +269,11 @@
"//topaz/packages/prod:chromium",
"//topaz/packages/prod:dart_jit_runner",
"//topaz/packages/prod:dart_jit_product_runner",
+ "//topaz/packages/prod:datetime_settings",
+ "//topaz/packages/prod:display_settings",
"//topaz/packages/prod:google_auth_provider",
"//topaz/packages/prod:mondrian",
+ "//topaz/packages/prod:settings",
"//topaz/packages/prod:userpicker_base_shell",
"//topaz/packages/prod:wifi_settings",
"//topaz/shell/ermine:ermine",
diff --git a/packages/tests/BUILD.gn b/packages/tests/BUILD.gn
index 75a7fb1..6c8a36b 100644
--- a/packages/tests/BUILD.gn
+++ b/packages/tests/BUILD.gn
@@ -22,6 +22,7 @@
testonly = true
public_deps = [
"//topaz/bin/dart_fidl_json/test:dart_fidl_json_test($host_toolchain)",
+ "//topaz/bin/settings:settings_tests($host_toolchain)",
"//topaz/examples/test/flutter_widget_test($host_toolchain)",
"//topaz/lib/keyboard/flutter:keyboard_test($host_toolchain)",
"//topaz/lib/setui/common:lib_setui_common_test($host_toolchain)",
diff --git a/shell/ermine/BUILD.gn b/shell/ermine/BUILD.gn
index 144314d..62fdb76 100644
--- a/shell/ermine/BUILD.gn
+++ b/shell/ermine/BUILD.gn
@@ -32,6 +32,7 @@
"main.dart",
"src/models/app_model.dart",
"src/models/ermine_service_provider.dart",
+ "src/models/default_proposer.dart",
"src/models/package_proposer.dart",
"src/models/story_manager.dart",
"src/models/story_model.dart",
diff --git a/shell/ermine/lib/src/models/app_model.dart b/shell/ermine/lib/src/models/app_model.dart
index 31c8e8c..c9d9a78 100644
--- a/shell/ermine/lib/src/models/app_model.dart
+++ b/shell/ermine/lib/src/models/app_model.dart
@@ -41,6 +41,7 @@
import '../utils/elevations.dart';
import '../utils/key_chord_listener.dart' show KeyChordListener;
import '../utils/session_shell_services.dart' show SessionShellServices;
+import 'default_proposer.dart' show DefaultProposer;
import 'ermine_service_provider.dart' show ErmineServiceProvider;
import 'package_proposer.dart' show PackageProposer;
import 'story_manager.dart'
@@ -64,6 +65,7 @@
final _componentControllerProxy = ComponentControllerProxy();
final _suggestionProvider = SuggestionProviderProxy();
final _ask = AskBarProxy();
+ final _defaultProposer = DefaultProposer();
final _packageProposer = PackageProposer();
final _webProposer = WebProposer();
@@ -88,6 +90,7 @@
.incoming
.connectToService(_componentContext);
StartupContext.fromStartupInfo().incoming.connectToService(_puppetMaster);
+ _defaultProposer.start();
_packageProposer.start();
_webProposer.start();
@@ -200,6 +203,7 @@
_sessionShellContext.logout();
storyManager.stop();
_pointerEventsListener.stop();
+ _defaultProposer.stop();
_packageProposer.stop();
_webProposer.stop();
diff --git a/shell/ermine/lib/src/models/default_proposer.dart b/shell/ermine/lib/src/models/default_proposer.dart
new file mode 100644
index 0000000..53ffef1
--- /dev/null
+++ b/shell/ermine/lib/src/models/default_proposer.dart
@@ -0,0 +1,77 @@
+// Copyright 2018 The Fuchsia 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:async';
+
+import 'package:fidl_fuchsia_modular/fidl_async.dart'
+ show
+ AddMod,
+ IntelligenceServicesProxy,
+ ProposalPublisherProxy,
+ QueryHandler,
+ QueryHandlerBinding,
+ QueryResponse,
+ SetFocusState,
+ StoryCommand,
+ SurfaceRelation,
+ UserInput;
+import 'package:fuchsia_modular/module.dart';
+import 'package:fuchsia_modular/proposal.dart';
+import 'package:fuchsia_services/services.dart';
+
+/// Proposes suggestions when no query is entered.
+class DefaultProposer extends QueryHandler {
+ final _proposalPublisherProxy = ProposalPublisherProxy();
+ final _queryHandlerBinding = QueryHandlerBinding();
+
+ /// Starts the proposal process.
+ void start() {
+ final intelligenceServicesProxy = IntelligenceServicesProxy();
+ StartupContext.fromStartupInfo()
+ .incoming
+ .connectToService(intelligenceServicesProxy);
+ intelligenceServicesProxy
+ ..getProposalPublisher(_proposalPublisherProxy.ctrl.request())
+ ..registerQueryHandler(
+ _queryHandlerBinding.wrap(this),
+ );
+ intelligenceServicesProxy.ctrl.close();
+ }
+
+ /// Stops the proposal process.
+ void stop() {
+ _proposalPublisherProxy.ctrl.close();
+ _queryHandlerBinding.close();
+ }
+
+ @override
+ Future<QueryResponse> onQuery(UserInput input) async {
+ List<Proposal> proposals = <Proposal>[];
+ if (input.text.isEmpty) {
+ // Open settings.
+ proposals = await Future.wait([_createPackageProposal('settings')]);
+ }
+
+ return QueryResponse(proposals: proposals);
+ }
+
+ Future<Proposal> _createPackageProposal(String package) async {
+ final packageUrl = 'fuchsia-pkg://fuchsia.com/$package#meta/$package.cmx';
+ final AddMod addMod = AddMod(
+ intent: Intent(action: '', handler: packageUrl),
+ surfaceParentModName: [],
+ modName: [packageUrl],
+ surfaceRelation: SurfaceRelation(),
+ );
+ return Proposal(
+ id: 'open_$package',
+ headline: 'open $package',
+ confidence: 0.9,
+ details: package,
+ )
+ ..addStoryCommand(StoryCommand.withAddMod(addMod))
+ ..addStoryCommand(
+ StoryCommand.withSetFocusState(SetFocusState(focused: true)));
+ }
+}
diff --git a/shell/ermine/lib/src/modules/ask_model.dart b/shell/ermine/lib/src/modules/ask_model.dart
index 117d8a6..775108d 100644
--- a/shell/ermine/lib/src/modules/ask_model.dart
+++ b/shell/ermine/lib/src/modules/ask_model.dart
@@ -100,6 +100,7 @@
..jump(0.8)
..target = 1.0;
controller.clear();
+ onQuery('');
}
void hide() {
@@ -163,10 +164,6 @@
}
void onQuery(String query) {
- if (query?.isEmpty ?? true) {
- return;
- }
-
_suggestions = <Suggestion>[];
final queryListenerBinding = QueryListenerBinding();