telemetry_bridge.dart 4.9 KB

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