[ermine] Migrate time zone widget to fuchsia.settings

Remove dependency on fuchsia.deprecatedtimezone FIDL services from
the time zone widget in the Ermine status bar.

Bug: 42386

Change-Id: Iac5549f9ff34d12f11ee3755bc852e179f003f13
diff --git a/session_shells/ermine/login_shell/BUILD.gn b/session_shells/ermine/login_shell/BUILD.gn
index 8ade5f5..9c54312 100644
--- a/session_shells/ermine/login_shell/BUILD.gn
+++ b/session_shells/ermine/login_shell/BUILD.gn
@@ -39,7 +39,6 @@
   ]
 
   deps = [
-    "//sdk/fidl/fuchsia.deprecatedtimezone",
     "//sdk/fidl/fuchsia.modular",
     "//sdk/fidl/fuchsia.modular.auth",
     "//sdk/fidl/fuchsia.netstack",
diff --git a/session_shells/ermine/settings/BUILD.gn b/session_shells/ermine/settings/BUILD.gn
index 2c5ffe8..c618482 100644
--- a/session_shells/ermine/settings/BUILD.gn
+++ b/session_shells/ermine/settings/BUILD.gn
@@ -22,10 +22,11 @@
 
   deps = [
     "//sdk/fidl/fuchsia.bluetooth.control",
-    "//sdk/fidl/fuchsia.deprecatedtimezone",
+    "//sdk/fidl/fuchsia.intl",
     "//sdk/fidl/fuchsia.media",
     "//sdk/fidl/fuchsia.memory",
     "//sdk/fidl/fuchsia.power",
+    "//sdk/fidl/fuchsia.settings",
     "//sdk/fidl/fuchsia.ui.brightness",
     "//src/experiences/lib/quickui",
     "//src/experiences/session_shells/ermine/internationalization",
diff --git a/session_shells/ermine/settings/lib/src/timezone.dart b/session_shells/ermine/settings/lib/src/timezone.dart
index 0d2783d..a0f1c24 100644
--- a/session_shells/ermine/settings/lib/src/timezone.dart
+++ b/session_shells/ermine/settings/lib/src/timezone.dart
@@ -2,12 +2,11 @@
 // 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:flutter/material.dart';
 
-import 'package:fidl_fuchsia_deprecatedtimezone/fidl_async.dart';
+import 'package:fidl_fuchsia_intl/fidl_async.dart';
+import 'package:fidl_fuchsia_settings/fidl_async.dart';
 import 'package:fidl_fuchsia_ui_remotewidgets/fidl_async.dart';
-import 'package:fuchsia_logger/logger.dart';
 import 'package:fuchsia_services/services.dart' show StartupContext;
 import 'package:internationalization/strings.dart';
 import 'package:quickui/quickui.dart';
@@ -22,30 +21,18 @@
 
   _TimeZoneModel model;
 
-  TimeZone({TimezoneProxy timezone, TimezoneWatcherBinding binding}) {
+  TimeZone({IntlProxy intlSettingsService}) {
     model = _TimeZoneModel(
-      timezone: timezone,
-      binding: binding,
+      intlSettingsService: intlSettingsService,
       onChange: _onChange,
     );
   }
 
   factory TimeZone.fromStartupContext(StartupContext startupContext) {
-    // Connect to timeservice and update the system time.
-    final timeService = TimeServiceProxy();
-    startupContext.incoming.connectToService(timeService);
-    timeService.update(3 /* num_retries */).then((success) {
-      if (!success) {
-        log.warning('Failed to update system time from the network.');
-      }
-      timeService.ctrl.close();
-    });
+    final intlSettingsService = IntlProxy();
+    startupContext.incoming.connectToService(intlSettingsService);
 
-    // Connect to timezone service.
-    final timezoneService = TimezoneProxy();
-    startupContext.incoming.connectToService(timezoneService);
-
-    final timezone = TimeZone(timezone: timezoneService);
+    final timezone = TimeZone(intlSettingsService: intlSettingsService);
     return timezone;
   }
 
@@ -63,7 +50,7 @@
         spec = _specForTimeZone(model, changeAction);
       } else {
         final index = value.text.action ^ QuickAction.submit.$value;
-        model.timezoneId = _kTimeZones[index].zoneId;
+        model.timeZoneId = _kTimeZones[index].zoneId;
         spec = _specForTimeZone(model);
       }
     }
@@ -79,7 +66,7 @@
       return Spec(title: _title, groups: [
         Group(title: _title, values: [
           Value.withText(TextValue(
-            text: model.timezoneId,
+            text: model.timeZoneId,
             action: changeAction,
           )),
         ]),
@@ -110,42 +97,42 @@
 }
 
 class _TimeZoneModel {
-  final TimezoneProxy timezone;
-  final TimezoneWatcherBinding _binding;
+  IntlProxy intlSettingsService;
   final VoidCallback onChange;
 
-  String _timezoneId;
+  IntlSettings _intlSettings;
 
-  _TimeZoneModel({this.timezone, TimezoneWatcherBinding binding, this.onChange})
-      : _binding = binding ?? TimezoneWatcherBinding() {
+  _TimeZoneModel({this.intlSettingsService, this.onChange}) {
     // Get current timezone and watch it for changes.
-    timezone
-      ..getTimezoneId().then((tz) {
-        _timezoneId = tz;
-        onChange();
-      })
-      ..watch(_binding.wrap(_TimezoneWatcherImpl(this)));
+    intlSettingsService.watch().then(_onIntlSettingsChange);
+  }
+
+  Future<void> _onIntlSettingsChange(IntlSettings intlSettings) async {
+    bool timeZoneChanged = (_intlSettings == null) ||
+        (_intlSettings.timeZoneId.id != intlSettings.timeZoneId.id);
+    _intlSettings = intlSettings;
+    if (timeZoneChanged) {
+      onChange();
+    }
+    // Use the FIDL "hanging get" pattern to request the next update.
+    if (intlSettingsService != null) {
+      await intlSettingsService.watch().then(_onIntlSettingsChange);
+    }
   }
 
   void dispose() {
-    timezone.ctrl.close();
-    _binding.close();
+    intlSettingsService.ctrl.close();
+    intlSettingsService = null;
   }
 
-  String get timezoneId => _timezoneId;
-  set timezoneId(String value) {
-    _timezoneId = value;
-    timezone.setTimezone(value);
-  }
-}
-
-class _TimezoneWatcherImpl extends TimezoneWatcher {
-  final _TimeZoneModel model;
-  _TimezoneWatcherImpl(this.model);
-  @override
-  Future<void> onTimezoneOffsetChange(String timezoneId) async {
-    model._timezoneId = timezoneId;
-    model.onChange();
+  String get timeZoneId =>
+      _intlSettings == null ? null : _intlSettings.timeZoneId.id;
+  set timeZoneId(String value) {
+    final IntlSettings newIntlSettings = IntlSettings(
+        locales: _intlSettings.locales,
+        temperatureUnit: _intlSettings.temperatureUnit,
+        timeZoneId: TimeZoneId(id: value));
+    intlSettingsService.set(newIntlSettings);
   }
 }
 
diff --git a/session_shells/ermine/settings/test/timezone_test.dart b/session_shells/ermine/settings/test/timezone_test.dart
index 5449c54..6cbd5b5 100644
--- a/session_shells/ermine/settings/test/timezone_test.dart
+++ b/session_shells/ermine/settings/test/timezone_test.dart
@@ -2,45 +2,61 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'package:fidl_fuchsia_deprecatedtimezone/fidl_async.dart';
-import 'package:fidl_fuchsia_ui_remotewidgets/fidl_async.dart';
+import 'package:fidl/fidl.dart';
+import 'package:fidl_fuchsia_intl/fidl_async.dart';
+import 'package:fidl_fuchsia_settings/fidl_async.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:mockito/mockito.dart';
 import 'package:settings/settings.dart';
 
 void main() {
+  MockIntlProxy intlSettingsProxy;
+  MockIntlProxyController intlSettingsProxyController;
+
+  setUp(() async {
+    intlSettingsProxy = MockIntlProxy();
+    intlSettingsProxyController = MockIntlProxyController();
+    when(intlSettingsProxy.ctrl).thenReturn(intlSettingsProxyController);
+  });
+
   test('Timezone', () async {
-    final timezoneProxy = MockTimezoneProxy();
-    final binding = MockBinding();
+    when(intlSettingsProxy.watch())
+        .thenAnswer((_) => Future<IntlSettings>.value(IntlSettings(
+              timeZoneId: TimeZoneId(id: 'Foo'),
+            )));
 
-    when(timezoneProxy.getTimezoneId())
-        .thenAnswer((_) => Future<String>.value('Foo'));
-
-    TimeZone timezone = TimeZone(timezone: timezoneProxy, binding: binding);
-    final spec = await timezone.getSpec();
+    TimeZone timeZone = TimeZone(intlSettingsService: intlSettingsProxy);
+    final spec = await timeZone.getSpec();
     expect(spec.groups.first.values.first.text.text == 'Foo', true);
+
+    timeZone.dispose();
   });
 
   test('Change Timezone', () async {
-    final timezoneProxy = MockTimezoneProxy();
-    final binding = MockBinding();
+    var response = 'tz1';
 
-    when(timezoneProxy.getTimezoneId())
-        .thenAnswer((_) => Future<String>.value('Foo'));
+    when(intlSettingsProxy.watch()).thenAnswer((_) {
+      return Future<IntlSettings>.value(IntlSettings(
+        timeZoneId: TimeZoneId(id: response),
+      ));
+    });
 
-    TimeZone timezone = TimeZone(timezone: timezoneProxy, binding: binding);
-    await timezone.getSpec();
+    TimeZone timeZone = TimeZone(intlSettingsService: intlSettingsProxy);
+    final specA = await timeZone.getSpec();
+    expect(specA.groups.first.values.first.text.text, 'tz1');
 
-    final spec = await timezone.getSpec(Value.withText(TextValue(
-      text: 'US/Eastern',
-      action: QuickAction.submit.$value,
-    )));
+    response = 'tz2';
+    // Wait one event cycle for the change.
+    await timeZone.getSpec();
+    final specB = await timeZone.getSpec();
+    expect(specB.groups.first.values.first.text.text, 'tz2');
 
-    expect(spec.groups.first.values.first.text.text == 'US/Eastern', true);
+    timeZone.dispose();
   });
 }
 
 // Mock classes.
-class MockTimezoneProxy extends Mock implements TimezoneProxy {}
+class MockIntlProxy extends Mock implements IntlProxy {}
 
-class MockBinding extends Mock implements TimezoneWatcherBinding {}
+class MockIntlProxyController extends Mock
+    implements AsyncProxyController<Intl> {}
diff --git a/session_shells/ermine/shell/meta/ermine.cmx b/session_shells/ermine/shell/meta/ermine.cmx
index 9a58d21..eda2f3e 100644
--- a/session_shells/ermine/shell/meta/ermine.cmx
+++ b/session_shells/ermine/shell/meta/ermine.cmx
@@ -14,8 +14,6 @@
             "fuchsia.app.discover.Suggestions",
             "fuchsia.bluetooth.control.Control",
             "fuchsia.cobalt.LoggerFactory",
-            "fuchsia.deprecatedtimezone.TimeService",
-            "fuchsia.deprecatedtimezone.Timezone",
             "fuchsia.device.manager.Administrator",
             "fuchsia.fonts.Provider",
             "fuchsia.intl.PropertyProvider",
@@ -28,6 +26,7 @@
             "fuchsia.modular.PuppetMaster",
             "fuchsia.modular.SessionShellContext",
             "fuchsia.power.BatteryManager",
+            "fuchsia.settings.Intl",
             "fuchsia.sys.Environment",
             "fuchsia.sys.Launcher",
             "fuchsia.ui.brightness.Control",