blob: 7defda58ce389030ec111e249558455fed10595f [file] [log] [blame]
// 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 'dart:async';
import 'package:fidl_fuchsia_power/fidl_async.dart';
import 'package:fidl_fuchsia_ui_remotewidgets/fidl_async.dart';
import 'package:flutter/material.dart';
import 'package:fuchsia_services/services.dart' show Incoming;
import 'package:internationalization/strings.dart';
import 'package:quickui/quickui.dart';
// ignore_for_file: prefer_constructors_over_static_methods
/// Defines a [UiSpec] for visualizing battery.
class Battery extends UiSpec {
// Localized strings.
static String get _title => Strings.battery;
late BatteryModel model;
Battery({
required BatteryManagerProxy monitor,
BatteryInfoWatcherBinding? binding,
}) {
model = BatteryModel(
monitor: monitor,
binding: binding,
onChange: _onChange,
);
}
factory Battery.withSvcPath() {
final batteryManager = BatteryManagerProxy();
Incoming.fromSvcPath().connectToService(batteryManager);
return Battery(monitor: batteryManager);
}
void _onChange() {
spec = _specForBattery(model.battery, model.charging);
}
@override
void update(Value value) async {}
@override
void dispose() {
model.dispose();
}
static Spec _specForBattery(double value, bool charging) {
if (value.isNaN) {
// Send nullSpec to hide battery settings.
return UiSpec.nullSpec;
}
final batteryText = '${value.toStringAsFixed(0)}%';
if (value == 100) {
return Spec(title: _title, groups: [
Group(
title: _title,
icon: IconValue(codePoint: Icons.battery_full.codePoint),
values: [Value.withText(TextValue(text: batteryText))],
),
]);
} else if (charging) {
return Spec(title: _title, groups: [
Group(
title: _title,
icon: IconValue(codePoint: Icons.battery_charging_full.codePoint),
values: [Value.withText(TextValue(text: batteryText))],
),
]);
} else if (value <= 10) {
return Spec(title: _title, groups: [
Group(
title: _title,
icon: IconValue(codePoint: Icons.battery_alert.codePoint),
values: [Value.withText(TextValue(text: batteryText))],
),
]);
} else {
return Spec(title: _title, groups: [
Group(
title: _title,
icon: IconValue(codePoint: Icons.battery_std.codePoint),
values: [Value.withText(TextValue(text: batteryText))],
),
]);
}
}
}
class BatteryModel {
final VoidCallback onChange;
final BatteryInfoWatcherBinding _binding;
final BatteryManagerProxy _monitor;
late double _battery;
late bool charging;
BatteryModel({
required this.onChange,
required BatteryManagerProxy monitor,
BatteryInfoWatcherBinding? binding,
}) : _binding = binding ?? BatteryInfoWatcherBinding(),
_monitor = monitor {
// Note that watcher will receive callback immediately with
// current battery info, so no need to make additional calls
// to get initial state.
_monitor.watch(_binding.wrap(_BatteryInfoWatcherImpl(this)));
}
void dispose() {
_monitor.ctrl.close();
_binding.close();
}
double get battery => _battery;
set battery(double value) {
_battery = value;
onChange();
}
void _updateBattery(BatteryInfo info) {
// BatteryStatus.ok indicates that the battery is present and
// in a known state (so we can show battery info).
// Alternate states include:
// BatteryStatus.unknown - not yet initialized
// (waiting for information from the system)
// BatteryStatus.notAvailable = battery present, but possibly disabled
// BatteryStatus.notPresent = batteries not included
if (info.status == BatteryStatus.ok) {
final chargeStatus = info.chargeStatus;
charging = chargeStatus == ChargeStatus.charging;
battery = info.levelPercent!;
} else if (info.status == BatteryStatus.notPresent) {
// upon receiving report of status 'notPresent' it is safe to close the
// connection and stop listening for battery status updates.
_monitor.ctrl.close();
_binding.close();
}
}
}
class _BatteryInfoWatcherImpl extends BatteryInfoWatcher {
final BatteryModel batteryModel;
_BatteryInfoWatcherImpl(this.batteryModel);
@override
Future<void> onChangeBatteryInfo(BatteryInfo info) async {
batteryModel._updateBattery(info);
}
}