system_status.dart 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. /// Battery and connectivity snapshot shown on the home screen.
  2. class SystemStatus {
  3. const SystemStatus({
  4. this.batteryLevel = 0,
  5. this.charging = false,
  6. this.wifi = false,
  7. });
  8. final int batteryLevel;
  9. final bool charging;
  10. final bool wifi;
  11. factory SystemStatus.fromMap(Map<dynamic, dynamic> map) {
  12. return SystemStatus(
  13. batteryLevel: (map['batteryLevel'] as num?)?.toInt() ?? 0,
  14. charging: map['charging'] == true,
  15. wifi: map['wifi'] == true,
  16. );
  17. }
  18. }
  19. /// Current media-stream volume and its maximum.
  20. class VolumeInfo {
  21. const VolumeInfo({
  22. this.index = 0,
  23. this.max = 15,
  24. this.min = 0,
  25. this.fixed = false,
  26. this.streams = const {},
  27. this.source = 'system',
  28. this.vendor = '',
  29. });
  30. final int index;
  31. final int max;
  32. /// Lowest index Android accepts for the stream. When it equals [max] the
  33. /// unit exposes no usable range at all.
  34. final int min;
  35. /// True when Android delegates volume to a fixed hardware amplifier (common
  36. /// on car head units). Apps cannot change that volume.
  37. final bool fixed;
  38. /// Raw indices for every stream, used by the on-screen diagnostics panel.
  39. final Map<String, int> streams;
  40. /// Which backend produced [index]: `system` for `AudioManager`, `syu` when
  41. /// the car's MCU volume module is in control.
  42. final String source;
  43. /// Native vendor state (binding/ready/unavailable), diagnostics only.
  44. final String vendor;
  45. factory VolumeInfo.fromMap(Map<dynamic, dynamic> map) {
  46. final rawStreams = map['streams'];
  47. final streams = <String, int>{};
  48. if (rawStreams is Map) {
  49. rawStreams.forEach((key, value) {
  50. if (key is String && value is num) {
  51. streams[key] = value.toInt();
  52. }
  53. });
  54. }
  55. return VolumeInfo(
  56. index: (map['index'] as num?)?.toInt() ?? 0,
  57. max: (map['max'] as num?)?.toInt() ?? 15,
  58. min: (map['min'] as num?)?.toInt() ?? 0,
  59. fixed: map['fixed'] == true,
  60. streams: streams,
  61. source: map['source'] is String ? map['source'] as String : 'system',
  62. vendor: map['vendor'] is String ? map['vendor'] as String : '',
  63. );
  64. }
  65. /// True when there is no range to move through, either because the firmware
  66. /// says so or because the stream reports a single step.
  67. bool get isControllable => !fixed && max > 0 && min < max;
  68. VolumeInfo copyWith({
  69. int? index,
  70. int? max,
  71. int? min,
  72. bool? fixed,
  73. Map<String, int>? streams,
  74. String? source,
  75. String? vendor,
  76. }) {
  77. return VolumeInfo(
  78. index: index ?? this.index,
  79. max: max ?? this.max,
  80. min: min ?? this.min,
  81. fixed: fixed ?? this.fixed,
  82. streams: streams ?? this.streams,
  83. source: source ?? this.source,
  84. vendor: vendor ?? this.vendor,
  85. );
  86. }
  87. double get fraction => max <= 0 ? 0 : (index / max).clamp(0.0, 1.0);
  88. int get percent => (fraction * 100).round();
  89. }