telemetry_bridge.dart 5.3 KB

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