telemetry_bridge.dart 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import 'package:flutter/services.dart';
  2. import '../models/bluetooth_device_info.dart';
  3. import '../models/location_reading.dart';
  4. import '../models/obd_state.dart';
  5. class TelemetryException implements Exception {
  6. TelemetryException(this.message);
  7. final String message;
  8. @override
  9. String toString() => message;
  10. }
  11. /// Bluetooth OBD and location boundary. Kept separate from LauncherPlatform so
  12. /// package/launcher fakes do not need telemetry methods.
  13. abstract class TelemetryPlatform {
  14. Future<List<BluetoothDeviceInfo>> listBondedDevices();
  15. Future<bool> isBluetoothEnabled();
  16. Future<bool> ensureBluetoothPermission();
  17. Future<bool> ensureLocationPermission();
  18. Future<LocationReading> getLocation();
  19. Future<void> connectObd(String address);
  20. Future<void> disconnectObd();
  21. Future<ObdState> getObdState();
  22. }
  23. class NativeTelemetryBridge implements TelemetryPlatform {
  24. static const _channel = MethodChannel('com.wasp.cardash_launcher/telemetry');
  25. @override
  26. Future<List<BluetoothDeviceInfo>> listBondedDevices() async {
  27. try {
  28. final raw = await _channel.invokeMethod<dynamic>('listBondedDevices');
  29. if (raw is! List) return const [];
  30. return raw
  31. .whereType<Map>()
  32. .map(BluetoothDeviceInfo.fromMap)
  33. .where((device) => device.address.isNotEmpty)
  34. .toList(growable: false);
  35. } on PlatformException catch (error) {
  36. throw TelemetryException(
  37. error.message ?? 'Could not list Bluetooth devices.',
  38. );
  39. } on MissingPluginException {
  40. throw TelemetryException('The telemetry bridge is unavailable.');
  41. }
  42. }
  43. @override
  44. Future<bool> isBluetoothEnabled() async {
  45. try {
  46. return await _channel.invokeMethod<bool>('isBluetoothEnabled') ?? false;
  47. } on PlatformException {
  48. return false;
  49. } on MissingPluginException {
  50. return false;
  51. }
  52. }
  53. @override
  54. Future<bool> ensureBluetoothPermission() async {
  55. try {
  56. return await _channel.invokeMethod<bool>('ensureBluetoothPermission') ??
  57. false;
  58. } on PlatformException {
  59. return false;
  60. } on MissingPluginException {
  61. return false;
  62. }
  63. }
  64. @override
  65. Future<bool> ensureLocationPermission() async {
  66. try {
  67. return await _channel.invokeMethod<bool>('ensureLocationPermission') ??
  68. false;
  69. } on PlatformException {
  70. return false;
  71. } on MissingPluginException {
  72. return false;
  73. }
  74. }
  75. @override
  76. Future<LocationReading> getLocation() async {
  77. try {
  78. final raw = await _channel.invokeMethod<dynamic>('getLocation');
  79. if (raw is! Map) {
  80. throw TelemetryException('Could not read the current location.');
  81. }
  82. return LocationReading.fromMap(raw);
  83. } on PlatformException catch (error) {
  84. throw TelemetryException(error.message ?? 'Could not read location.');
  85. } on MissingPluginException {
  86. throw TelemetryException('The telemetry bridge is unavailable.');
  87. }
  88. }
  89. @override
  90. Future<void> connectObd(String address) async {
  91. try {
  92. await _channel.invokeMethod<dynamic>('connectObd', {'address': address});
  93. } on PlatformException catch (error) {
  94. throw TelemetryException(error.message ?? 'Could not connect to OBD.');
  95. } on MissingPluginException {
  96. throw TelemetryException('The telemetry bridge is unavailable.');
  97. }
  98. }
  99. @override
  100. Future<void> disconnectObd() async {
  101. try {
  102. await _channel.invokeMethod<dynamic>('disconnectObd');
  103. } on PlatformException {
  104. // Nothing useful to do when already disconnected.
  105. } on MissingPluginException {
  106. // No-op outside Android.
  107. }
  108. }
  109. @override
  110. Future<ObdState> getObdState() async {
  111. try {
  112. final raw = await _channel.invokeMethod<dynamic>('getObdState');
  113. return raw is Map ? ObdState.fromMap(raw) : const ObdState();
  114. } on PlatformException {
  115. return const ObdState();
  116. } on MissingPluginException {
  117. return const ObdState();
  118. }
  119. }
  120. }