From bb622ec2261da91e817f3cb920490e688f1ee72f Mon Sep 17 00:00:00 2001 From: Philipp Lepold Date: Mon, 11 Dec 2023 12:58:53 +0100 Subject: [PATCH 01/15] added "download Android app" button and made buttons more visually appealing --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 46c260c..8daeb92 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,21 @@ [OpenEarable](https://open-earable.teco.edu) is a new, open-source, Arduino-based platform for ear-based sensing applications. It provides a versatile prototyping platform with support for various sensors and actuators, making it suitable for earable research and development. -
 [Download iOS beta app!](https://testflight.apple.com/join/Kht3e1Cb) 
+
+ + ⬇️ Download iOS beta app + + + ⬇️ Download Android app + +
-
 [Get OpenEarable device now!](https://forms.gle/R3LMcqtyKwVH7PZB9) 
+
+ + 🦻 Get OpenEarable device now + +
## Table of Contents - [Introduction](#Introduction) From f419e474992026cfe10feebf4ac33bdd41e8db1f Mon Sep 17 00:00:00 2001 From: Philipp <35779536+ilteen@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:36:15 +0100 Subject: [PATCH 02/15] Forgot that github doesn't render css... Changed download buttons again --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8daeb92..ba1986a 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,23 @@ [OpenEarable](https://open-earable.teco.edu) is a new, open-source, Arduino-based platform for ear-based sensing applications. It provides a versatile prototyping platform with support for various sensors and actuators, making it suitable for earable research and development. -
- +

+ ⬇️ Download iOS beta app - - - + +

+ +

+ ⬇️ Download Android app -

+

-
- +

+ 🦻 Get OpenEarable device now -

+

## Table of Contents - [Introduction](#Introduction) From 45f22b3e3bb2f13d3327937cd28f66cd9d7989bb Mon Sep 17 00:00:00 2001 From: Dennis <45356478+DennisMoschina@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:31:12 +0100 Subject: [PATCH 03/15] display posture status via rgb led of earable --- open_earable/ios/Podfile.lock | 8 +------- .../posture_tracker/model/bad_posture_reminder.dart | 11 +++++++++++ .../model/earable_attitude_tracker.dart | 1 + 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/open_earable/ios/Podfile.lock b/open_earable/ios/Podfile.lock index ea196ab..7d44463 100644 --- a/open_earable/ios/Podfile.lock +++ b/open_earable/ios/Podfile.lock @@ -5,8 +5,6 @@ PODS: - three3d_egl (~> 0.1.3) - flutter_native_splash (0.0.1): - Flutter - - location (0.0.1): - - Flutter - open_file (0.0.1): - Flutter - path_provider_foundation (0.0.1): @@ -26,7 +24,6 @@ DEPENDENCIES: - Flutter (from `Flutter`) - flutter_gl (from `.symlinks/plugins/flutter_gl/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - - location (from `.symlinks/plugins/location/ios`) - open_file (from `.symlinks/plugins/open_file/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) @@ -45,8 +42,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_gl/ios" flutter_native_splash: :path: ".symlinks/plugins/flutter_native_splash/ios" - location: - :path: ".symlinks/plugins/location/ios" open_file: :path: ".symlinks/plugins/open_file/ios" path_provider_foundation: @@ -60,7 +55,6 @@ SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_gl: 5a5603f35db897697f064027864a32b15d0c421d flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef - location: d5cf8598915965547c3f36761ae9cc4f4e87d22e open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 @@ -71,4 +65,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart b/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart index 4d3fe70..cfeaddf 100644 --- a/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart +++ b/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart @@ -50,6 +50,8 @@ class BadPostureReminder { BadPostureReminder(this._openEarable, this._attitudeTracker); + bool? _lastPostureWasBad = null; + void start() { _timestamps.lastReset = DateTime.now(); _timestamps.lastBadPosture = null; @@ -64,6 +66,10 @@ class BadPostureReminder { DateTime now = DateTime.now(); if (_isBadPosture(attitude)) { + if (!(_lastPostureWasBad ?? false)) { + _openEarable.rgbLed.writeLedColor(r: 255, g: 0, b: 0); + } + // If this is the first time the program enters the bad state, store the current time if (_timestamps.lastBadPosture == null) { _timestamps.lastBadPosture = now; @@ -81,6 +87,10 @@ class BadPostureReminder { // Reset the last good state time _timestamps.lastGoodPosture = null; } else { + if (_lastPostureWasBad ?? false) { + _openEarable.rgbLed.writeLedColor(r: 0, g: 255, b: 0); + } + // If this is the first time the program enters the good state, store the current time if (_timestamps.lastGoodPosture == null) { _timestamps.lastGoodPosture = now; @@ -95,6 +105,7 @@ class BadPostureReminder { } } } + _lastPostureWasBad = _isBadPosture(attitude); }); } diff --git a/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart b/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart index 99ce712..f201725 100644 --- a/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart +++ b/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart @@ -41,6 +41,7 @@ class EarableAttitudeTracker extends AttitudeTracker { @override void stop() { + _openEarable.rgbLed.writeLedColor(r: 0, g: 0, b: 0); _subscription?.pause(); } From decbb6d146ab1b56b83a767ee3b689a6d5d585b8 Mon Sep 17 00:00:00 2001 From: Dennis <45356478+DennisMoschina@users.noreply.github.com> Date: Tue, 12 Dec 2023 12:49:56 +0100 Subject: [PATCH 04/15] fixed memory leak in postureTracker --- .../model/bad_posture_reminder.dart | 7 +++++++ .../model/earable_attitude_tracker.dart | 2 +- .../view/posture_tracker_view.dart | 16 ++++------------ .../view_model/posture_tracker_view_model.dart | 2 ++ 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart b/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart index cfeaddf..56b697a 100644 --- a/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart +++ b/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart @@ -108,6 +108,13 @@ class BadPostureReminder { _lastPostureWasBad = _isBadPosture(attitude); }); } + + void stop() { + _timestamps.lastBadPosture = null; + _timestamps.lastGoodPosture = null; + _openEarable.rgbLed.writeLedColor(r: 0, g: 0, b: 0); + _attitudeTracker.stop(); + } void setSettings(BadPostureSettings settings) { _settings = settings; diff --git a/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart b/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart index f201725..187b6ca 100644 --- a/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart +++ b/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart @@ -41,12 +41,12 @@ class EarableAttitudeTracker extends AttitudeTracker { @override void stop() { - _openEarable.rgbLed.writeLedColor(r: 0, g: 0, b: 0); _subscription?.pause(); } @override void cancle() { + stop(); _subscription?.cancel(); super.cancle(); } diff --git a/open_earable/lib/apps/posture_tracker/view/posture_tracker_view.dart b/open_earable/lib/apps/posture_tracker/view/posture_tracker_view.dart index 67b465e..21490f3 100644 --- a/open_earable/lib/apps/posture_tracker/view/posture_tracker_view.dart +++ b/open_earable/lib/apps/posture_tracker/view/posture_tracker_view.dart @@ -22,25 +22,17 @@ class PostureTrackerView extends StatefulWidget { } class _PostureTrackerViewState extends State { - late final PostureTrackerViewModel _viewModel; - - @override - void initState() { - super.initState(); - this._viewModel = PostureTrackerViewModel(widget._tracker, BadPostureReminder(widget._openEarable, widget._tracker)); - } - @override Widget build(BuildContext context) { - return ChangeNotifierProvider.value( - value: _viewModel, + return ChangeNotifierProvider( + create: (context) => PostureTrackerViewModel(widget._tracker, BadPostureReminder(widget._openEarable, widget._tracker)), builder: (context, child) => Consumer( builder: (context, postureTrackerViewModel, child) => Scaffold( appBar: AppBar( title: const Text("Posture Tracker"), actions: [ IconButton( - onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => SettingsView(this._viewModel))), + onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => SettingsView(postureTrackerViewModel))), icon: Icon(Icons.settings) ), ], @@ -103,7 +95,7 @@ class _PostureTrackerViewState extends State { return Column(children: [ ElevatedButton( onPressed: postureTrackerViewModel.isAvailable - ? () { postureTrackerViewModel.isTracking ? this._viewModel.stopTracking() : this._viewModel.startTracking(); } + ? () { postureTrackerViewModel.isTracking ? postureTrackerViewModel.stopTracking() : postureTrackerViewModel.startTracking(); } : null, style: ElevatedButton.styleFrom( backgroundColor: !postureTrackerViewModel.isTracking ? Color(0xff77F2A1) : Color(0xfff27777), diff --git a/open_earable/lib/apps/posture_tracker/view_model/posture_tracker_view_model.dart b/open_earable/lib/apps/posture_tracker/view_model/posture_tracker_view_model.dart index 1263e8a..0f07bc2 100644 --- a/open_earable/lib/apps/posture_tracker/view_model/posture_tracker_view_model.dart +++ b/open_earable/lib/apps/posture_tracker/view_model/posture_tracker_view_model.dart @@ -38,6 +38,7 @@ class PostureTrackerViewModel extends ChangeNotifier { void stopTracking() { _attitudeTracker.stop(); + _badPostureReminder.stop(); notifyListeners(); } @@ -51,6 +52,7 @@ class PostureTrackerViewModel extends ChangeNotifier { @override void dispose() { + stopTracking(); _attitudeTracker.cancle(); super.dispose(); } From bc5308a91011dee0ea41a949892a15bd3f8e563c Mon Sep 17 00:00:00 2001 From: Dennis <45356478+DennisMoschina@users.noreply.github.com> Date: Tue, 12 Dec 2023 13:10:12 +0100 Subject: [PATCH 05/15] added ewma filter for posture tracker --- .../posture_tracker/model/bad_posture_reminder.dart | 4 ++-- .../model/earable_attitude_tracker.dart | 13 +++++++++---- .../lib/apps/posture_tracker/model/ewma.dart | 11 +++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 open_earable/lib/apps/posture_tracker/model/ewma.dart diff --git a/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart b/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart index 56b697a..c2bd521 100644 --- a/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart +++ b/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart @@ -37,8 +37,8 @@ class PostureTimestamps { class BadPostureReminder { BadPostureSettings _settings = BadPostureSettings( - rollAngleThreshold: 7, - pitchAngleThreshold: 15, + rollAngleThreshold: 20, + pitchAngleThreshold: 20, timeThreshold: 10, resetTimeThreshold: 2 ); diff --git a/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart b/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart index 187b6ca..507efc3 100644 --- a/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart +++ b/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart @@ -1,6 +1,8 @@ import 'dart:async'; +import 'package:open_earable/apps/posture_tracker/model/attitude.dart'; import 'package:open_earable/apps/posture_tracker/model/attitude_tracker.dart'; +import 'package:open_earable/apps/posture_tracker/model/ewma.dart'; import 'package:open_earable_flutter/src/open_earable_flutter.dart'; class EarableAttitudeTracker extends AttitudeTracker { @@ -12,6 +14,10 @@ class EarableAttitudeTracker extends AttitudeTracker { @override bool get isAvailable => _openEarable.bleManager.connected; + EWMA _rollEWMA = EWMA(0.5); + EWMA _pitchEWMA = EWMA(0.5); + EWMA _yawEWMA = EWMA(0.5); + EarableAttitudeTracker(this._openEarable) { _openEarable.bleManager.connectionStateStream.listen((connected) { didChangeAvailability(this); @@ -21,7 +27,6 @@ class EarableAttitudeTracker extends AttitudeTracker { }); } - @override void start() { if (_subscription?.isPaused ?? false) { @@ -32,9 +37,9 @@ class EarableAttitudeTracker extends AttitudeTracker { _openEarable.sensorManager.writeSensorConfig(_buildSensorConfig()); _subscription = _openEarable.sensorManager.subscribeToSensorData(0).listen((event) { updateAttitude( - roll: event["EULER"]["ROLL"], - pitch: event["EULER"]["PITCH"], - yaw: event["EULER"]["YAW"] + roll: _rollEWMA.update(event["EULER"]["ROLL"]), + pitch: _pitchEWMA.update(event["EULER"]["PITCH"]), + yaw: _yawEWMA.update(event["EULER"]["YAW"]) ); }); } diff --git a/open_earable/lib/apps/posture_tracker/model/ewma.dart b/open_earable/lib/apps/posture_tracker/model/ewma.dart new file mode 100644 index 0000000..da2d62f --- /dev/null +++ b/open_earable/lib/apps/posture_tracker/model/ewma.dart @@ -0,0 +1,11 @@ +class EWMA { + double _alpha; + double _oldValue = 0; + + EWMA(this._alpha); + + double update(double newValue) { + _oldValue = _alpha * newValue + (1 - _alpha) * _oldValue; + return _oldValue; + } +} \ No newline at end of file From 121990b137495efbba91fd557ea45b19fca6a4d8 Mon Sep 17 00:00:00 2001 From: Dennis <45356478+DennisMoschina@users.noreply.github.com> Date: Tue, 12 Dec 2023 13:25:19 +0100 Subject: [PATCH 06/15] updated default values --- .../apps/posture_tracker/model/bad_posture_reminder.dart | 6 ++++-- .../posture_tracker/model/earable_attitude_tracker.dart | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart b/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart index c2bd521..d494d04 100644 --- a/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart +++ b/open_earable/lib/apps/posture_tracker/model/bad_posture_reminder.dart @@ -40,7 +40,7 @@ class BadPostureReminder { rollAngleThreshold: 20, pitchAngleThreshold: 20, timeThreshold: 10, - resetTimeThreshold: 2 + resetTimeThreshold: 1 ); final OpenEarable _openEarable; final AttitudeTracker _attitudeTracker; @@ -100,7 +100,9 @@ class BadPostureReminder { // Calculate the duration in seconds int duration = now.difference(_timestamps.lastGoodPosture!).inSeconds; // If the duration exceeds the minimum required, reset the last bad state time - if (duration > _settings.resetTimeThreshold) { + if (duration >= _settings.resetTimeThreshold) { + print("duration: $duration, reset time threshold: ${_settings.resetTimeThreshold}"); + print("resetting last bad posture time"); _timestamps.lastBadPosture = null; } } diff --git a/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart b/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart index 507efc3..0e151e4 100644 --- a/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart +++ b/open_earable/lib/apps/posture_tracker/model/earable_attitude_tracker.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:open_earable/apps/posture_tracker/model/attitude.dart'; import 'package:open_earable/apps/posture_tracker/model/attitude_tracker.dart'; import 'package:open_earable/apps/posture_tracker/model/ewma.dart'; import 'package:open_earable_flutter/src/open_earable_flutter.dart'; From 72afec42429c1b674a3b98bac9bf7331db9166a4 Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Tue, 12 Dec 2023 16:29:28 +0100 Subject: [PATCH 07/15] split up barometer and temperature data into two different tabs --- .../lib/sensor_data_tab/sensor_chart.dart | 200 +++++++----------- .../lib/sensor_data_tab/sensor_data_tab.dart | 24 ++- 2 files changed, 87 insertions(+), 137 deletions(-) diff --git a/open_earable/lib/sensor_data_tab/sensor_chart.dart b/open_earable/lib/sensor_data_tab/sensor_chart.dart index 3043b07..9327df4 100644 --- a/open_earable/lib/sensor_data_tab/sensor_chart.dart +++ b/open_earable/lib/sensor_data_tab/sensor_chart.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:flutter/scheduler.dart'; import 'package:open_earable_flutter/src/open_earable_flutter.dart'; import 'package:flutter/material.dart'; import 'package:charts_flutter/flutter.dart' as charts; @@ -20,7 +21,7 @@ class EarableDataChart extends StatefulWidget { class _EarableDataChartState extends State { final OpenEarable _openEarable; final String _title; - late List _data; + late List _data; StreamSubscription? _dataSubscription; _EarableDataChartState(this._openEarable, this._title); late int _minX = 0; @@ -34,22 +35,18 @@ class _EarableDataChartState extends State { late String _sensorName; int _numDatapoints = 200; _setupListeners() { - if (_title == "Pressure Data") { + if (_title == "Pressure" || _title == "Temperature") { _dataSubscription = _openEarable.sensorManager.subscribeToSensorData(1).listen((data) { - Map units = {}; - var baroUnits = data["BARO"]["units"]; - baroUnits["Pressure"] = - "k${baroUnits["Pressure"]}"; //Use kPA instead of Pa for chart - units.addAll(baroUnits); - units.addAll(data["TEMP"]["units"]); + //units.addAll(data["TEMP"]["units"]); int timestamp = data["timestamp"]; - BarometerValue barometerValue = BarometerValue( + SensorData sensorData = SensorData( + name: _sensorName, timestamp: timestamp, - pressure: data["BARO"]["Pressure"], - temperature: data["TEMP"]["Temperature"], - units: units); - _updateData(barometerValue); + values: [data[_sensorName][_title]], + //temperature: data["TEMP"]["Temperature"], + units: data[_sensorName]["units"]); + _updateData(sensorData); }); } else { kalmanX = SimpleKalman( @@ -67,31 +64,14 @@ class _EarableDataChartState extends State { _dataSubscription = _openEarable.sensorManager.subscribeToSensorData(0).listen((data) { int timestamp = data["timestamp"]; - /* - XYZValue accelerometerValue = XYZValue( - timestamp: timestamp, - x: data["ACC"]["X"], - y: data["ACC"]["Y"], - z: data["ACC"]["Z"], - units: data["ACC"]["units"]); - XYZValue gyroscopeValue = XYZValue( - timestamp: timestamp, - x: data["GYRO"]["X"], - y: data["GYRO"]["Y"], - z: data["GYRO"]["Z"], - units: data["GYRO"]["units"]); - XYZValue magnetometerValue = XYZValue( - timestamp: timestamp, - x: data["MAG"]["X"], - y: data["MAG"]["Y"], - z: data["MAG"]["Z"], - units: data["MAG"]["units"]); - */ - XYZValue xyzValue = XYZValue( + SensorData xyzValue = SensorData( + name: _title, timestamp: timestamp, - z: kalmanZ.filtered(data[_sensorName]["Z"]), - x: kalmanX.filtered(data[_sensorName]["X"]), - y: kalmanY.filtered(data[_sensorName]["Y"]), + values: [ + kalmanX.filtered(data[_sensorName]["X"]), + kalmanY.filtered(data[_sensorName]["Y"]), + kalmanZ.filtered(data[_sensorName]["Z"]), + ], units: data[_sensorName]["units"]); _updateData(xyzValue); @@ -99,34 +79,40 @@ class _EarableDataChartState extends State { } } - _updateData(DataValue value) { + _updateData(SensorData value) { setState(() { _data.add(value); _checkLength(_data); - DataValue? maxXYZValue = maxBy(_data, (DataValue b) => b.getMax()); - DataValue? minXYZValue = minBy(_data, (DataValue b) => b.getMin()); + SensorData? maxXYZValue = maxBy(_data, (SensorData b) => b.getMax()); + SensorData? minXYZValue = minBy(_data, (SensorData b) => b.getMin()); if (maxXYZValue == null || minXYZValue == null) { return; } double maxAbsValue = max(maxXYZValue.getMax().abs(), minXYZValue.getMin().abs()); - _maxY = maxAbsValue; + _maxY = (_title == "Pressure" || _title == "Temperature") + ? max(0, maxXYZValue.getMax()) + : maxAbsValue; - _minY = -maxAbsValue; + _minY = (_title == "Pressure" || _title == "Temperature") + ? min(0, minXYZValue.getMin()) + : -maxAbsValue; _maxX = value.timestamp; _minX = _data[0].timestamp; }); } _getColor(String title) { - if (title == "Accelerometer Data") { + if (title == "Accelerometer") { return ['#FF6347', '#3CB371', '#1E90FF']; - } else if (title == "Gyroscope Data") { + } else if (title == "Gyroscope") { return ['#FFD700', '#FF4500', '#D8BFD8']; - } else if (title == "Magnetometer Data") { + } else if (title == "Magnetometer") { return ['#F08080', '#98FB98', '#ADD8E6']; - } else if (title == "Pressure Data") { - return ['#32CD32', '#FFA07A']; + } else if (title == "Pressure") { + return ['#32CD32']; + } else if (title == "Temperature") { + return ['#FFA07A']; } } @@ -135,20 +121,25 @@ class _EarableDataChartState extends State { super.initState(); _data = []; switch (_title) { - case 'Pressure Data': + case 'Pressure': _sensorName = 'BARO'; - case 'Accelerometer Data': + case 'Temperature': + _sensorName = 'TEMP'; + case 'Accelerometer': _sensorName = 'ACC'; - case 'Gyroscope Data': + case 'Gyroscope': _sensorName = 'GYRO'; - case 'Magnetometer Data': + case 'Magnetometer': _sensorName = 'MAG'; } colors = _getColor(_title); - if (_title == 'Pressure Data') { + if (_title == 'Temperature') { + _minY = 0; + _maxY = 30; + } else if (_title == 'Pressure') { _minY = 0; - _maxY = 130; - } else if (_title == "Magnetometer Data") { + _maxY = 130000; + } else if (_title == "Magnetometer") { _minY = -200; _maxY = 200; } else { @@ -172,46 +163,37 @@ class _EarableDataChartState extends State { @override Widget build(BuildContext context) { - if (_title == 'Pressure Data') { + if (_title == 'Pressure' || _title == 'Temperature') { seriesList = [ - charts.Series( - id: 'Pressure${_data.isNotEmpty ? " (${_data[0].units['Pressure']})" : ""}', + charts.Series( + id: '$_title${_data.isNotEmpty ? " (${_data[0].units[_title]})" : ""}', colorFn: (_, __) => charts.Color.fromHex(code: colors[0]), - domainFn: (DataValue data, _) => data.timestamp, - measureFn: (DataValue data, _) => - (data as BarometerValue).pressure / 1000, - data: _data, - ), - charts.Series( - id: 'Temperature${_data.isNotEmpty ? " (${_data[0].units['Temperature']})" : ""}', - colorFn: (_, __) => charts.Color.fromHex(code: colors[1]), - domainFn: (DataValue data, _) => data.timestamp, - measureFn: (DataValue data, _) => - (data as BarometerValue).temperature, + domainFn: (SensorData data, _) => data.timestamp, + measureFn: (SensorData data, _) => data.values[0], data: _data, ), ]; } else { seriesList = [ - charts.Series( + charts.Series( id: 'X${_data.isNotEmpty ? " (${_data[0].units['X']})" : ""}', colorFn: (_, __) => charts.Color.fromHex(code: colors[0]), - domainFn: (DataValue data, _) => data.timestamp, - measureFn: (DataValue data, _) => (data as XYZValue).x, + domainFn: (SensorData data, _) => data.timestamp, + measureFn: (SensorData data, _) => data.values[0], data: _data, ), - charts.Series( + charts.Series( id: 'Y${_data.isNotEmpty ? " (${_data[0].units['Y']})" : ""}', colorFn: (_, __) => charts.Color.fromHex(code: colors[1]), - domainFn: (DataValue data, _) => data.timestamp, - measureFn: (DataValue data, _) => (data as XYZValue).y, + domainFn: (SensorData data, _) => data.timestamp, + measureFn: (SensorData data, _) => data.values[1], data: _data, ), - charts.Series( + charts.Series( id: 'Z${_data.isNotEmpty ? " (${_data[0].units['Z']})" : ""}', colorFn: (_, __) => charts.Color.fromHex(code: colors[2]), - domainFn: (DataValue data, _) => data.timestamp, - measureFn: (DataValue data, _) => (data as XYZValue).z, + domainFn: (SensorData data, _) => data.timestamp, + measureFn: (SensorData data, _) => data.values[2], data: _data, ), ]; @@ -243,6 +225,8 @@ class _EarableDataChartState extends State { ) ], primaryMeasureAxis: charts.NumericAxisSpec( + tickProviderSpec: + charts.BasicNumericTickProviderSpec(desiredTickCount: 7), renderSpec: charts.GridlineRendererSpec( labelStyle: charts.TextStyleSpec( fontSize: 14, @@ -266,66 +250,30 @@ class _EarableDataChartState extends State { } } -abstract class DataValue { +class SensorData { + final String name; final int timestamp; + final List values; final Map units; - double getMin(); - double getMax(); - DataValue({required this.timestamp, required this.units}); -} - -class XYZValue extends DataValue { - final double x; - final double y; - final double z; - XYZValue( - {required timestamp, - required this.x, - required this.y, - required this.z, - required units}) - : super(timestamp: timestamp, units: units); + SensorData( + {required this.name, + required this.timestamp, + required this.values, + required this.units}); - @override double getMax() { - return max(x, max(y, z)); - } - - @override - double getMin() { - return min(x, min(y, z)); + return values.reduce( + (currentMax, element) => element > currentMax ? element : currentMax); } - @override - String toString() { - return "timestamp: $timestamp\nx: $x, y: $y, z: $z"; - } -} - -class BarometerValue extends DataValue { - final double pressure; - final double temperature; - - BarometerValue( - {required timestamp, - required this.pressure, - required this.temperature, - required units}) - : super(timestamp: timestamp, units: units); - - @override double getMin() { - return min(pressure / 1000, temperature); - } - - @override - double getMax() { - return max(pressure / 1000, temperature); + return values.reduce( + (currentMin, element) => element < currentMin ? element : currentMin); } @override String toString() { - return "timestamp: $timestamp\npressure: $pressure, temperature:$temperature"; + return "sensor name: $name\ntimestamp: $timestamp\nvalues: ${values.join(", ")}"; } } diff --git a/open_earable/lib/sensor_data_tab/sensor_data_tab.dart b/open_earable/lib/sensor_data_tab/sensor_data_tab.dart index 861b3b4..836255b 100644 --- a/open_earable/lib/sensor_data_tab/sensor_data_tab.dart +++ b/open_earable/lib/sensor_data_tab/sensor_data_tab.dart @@ -20,17 +20,17 @@ class _SensorDataTabState extends State StreamSubscription? _batteryLevelSubscription; StreamSubscription? _buttonStateSubscription; - List accelerometerData = []; - List gyroscopeData = []; - List magnetometerData = []; - List barometerData = []; + List accelerometerData = []; + List gyroscopeData = []; + List magnetometerData = []; + List barometerData = []; _SensorDataTabState(this._openEarable); @override void initState() { super.initState(); - _tabController = TabController(vsync: this, length: 5); + _tabController = TabController(vsync: this, length: 6); if (_openEarable.bleManager.connected) { _setupListeners(); } @@ -108,8 +108,9 @@ class _SensorDataTabState extends State tabs: [ Tab(text: 'Accel.'), Tab(text: 'Gyro.'), - Tab(text: 'Magnet.'), - Tab(text: 'Pressure'), + Tab(text: 'Mag.'), + Tab(text: 'Baro.'), + Tab(text: 'Temp.'), Tab(text: '3D'), ], ), @@ -117,10 +118,11 @@ class _SensorDataTabState extends State body: TabBarView( controller: _tabController, children: [ - EarableDataChart(_openEarable, 'Accelerometer Data'), - EarableDataChart(_openEarable, 'Gyroscope Data'), - EarableDataChart(_openEarable, 'Magnetometer Data'), - EarableDataChart(_openEarable, 'Pressure Data'), + EarableDataChart(_openEarable, 'Accelerometer'), + EarableDataChart(_openEarable, 'Gyroscope'), + EarableDataChart(_openEarable, 'Magnetometer'), + EarableDataChart(_openEarable, 'Pressure'), + EarableDataChart(_openEarable, 'Temperature'), Earable3DModel(_openEarable), ], ), From 4ea79a101b5eed694c79c4712100b91c135b53f2 Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Tue, 12 Dec 2023 16:47:10 +0100 Subject: [PATCH 08/15] fix overflow issues of waveform picker --- .../lib/controls_tab/views/audio_player.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/open_earable/lib/controls_tab/views/audio_player.dart b/open_earable/lib/controls_tab/views/audio_player.dart index b18c04b..7674090 100644 --- a/open_earable/lib/controls_tab/views/audio_player.dart +++ b/open_earable/lib/controls_tab/views/audio_player.dart @@ -311,7 +311,7 @@ class _AudioPlayerCardState extends State { ), SizedBox( height: 37.0, - width: 80, + width: 75, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 0), child: TextField( @@ -411,9 +411,13 @@ class _AudioPlayerCardState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - _waveFormTextController.text, - style: TextStyle(fontSize: 16.0), + Expanded( + child: Text( + _waveFormTextController.text, + style: TextStyle(fontSize: 16.0), + overflow: TextOverflow.ellipsis, + softWrap: false, + ), ), Icon(Icons.arrow_drop_down), ], From 7d1a07e3378479991d075bb9c6cf29bdba4bb610 Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Tue, 12 Dec 2023 17:30:33 +0100 Subject: [PATCH 09/15] change colors --- .../view/posture_roll_view.dart | 69 +++---- .../view/posture_tracker_view.dart | 103 +++++----- .../posture_tracker/view/settings_view.dart | 185 +++++++++--------- .../lib/controls_tab/views/audio_player.dart | 2 +- .../lib/controls_tab/views/connect.dart | 2 +- .../lib/controls_tab/views/led_color.dart | 2 +- .../views/sensor_configuration.dart | 5 +- open_earable/lib/main.dart | 8 +- 8 files changed, 192 insertions(+), 184 deletions(-) diff --git a/open_earable/lib/apps/posture_tracker/view/posture_roll_view.dart b/open_earable/lib/apps/posture_tracker/view/posture_roll_view.dart index 24051f1..1bab1cd 100644 --- a/open_earable/lib/apps/posture_tracker/view/posture_roll_view.dart +++ b/open_earable/lib/apps/posture_tracker/view/posture_roll_view.dart @@ -17,44 +17,45 @@ class PostureRollView extends StatelessWidget { final String neckAssetPath; final AlignmentGeometry headAlignment; - const PostureRollView({Key? key, - required this.roll, - this.angleThreshold = 0, - required this.headAssetPath, - required this.neckAssetPath, - this.headAlignment = Alignment.center}) : super(key: key); + const PostureRollView( + {Key? key, + required this.roll, + this.angleThreshold = 0, + required this.headAssetPath, + required this.neckAssetPath, + this.headAlignment = Alignment.center}) + : super(key: key); @override Widget build(BuildContext context) { return Column(children: [ - Text( - "${(this.roll * 180 / 3.14).abs().toStringAsFixed(0)}°", - style: TextStyle( - // use proper color matching the background - color: Theme.of(context).colorScheme.onBackground, - fontSize: 30, - fontWeight: FontWeight.bold - ) - ), + Text("${(this.roll * 180 / 3.14).abs().toStringAsFixed(0)}°", + style: TextStyle( + // use proper color matching the background + color: Theme.of(context).colorScheme.onBackground, + fontSize: 30, + fontWeight: FontWeight.bold)), CustomPaint( - painter: ArcPainter(angle: this.roll, angleThreshold: this.angleThreshold), - child: Padding( - padding: EdgeInsets.all(10), - child: ClipOval( - child: Container( - color: roll.abs() > _MAX_ROLL ? Colors.red.withOpacity(0.5) : Colors.transparent, - child: Stack(children: [ - Image.asset(this.neckAssetPath), - Transform.rotate( - angle: this.roll.isFinite ? roll.abs() < _MAX_ROLL ? this.roll : roll.sign * _MAX_ROLL : 0, - alignment: this.headAlignment, - child: Image.asset(this.headAssetPath) - ), - ]) - ) - ) - ) - ), + painter: + ArcPainter(angle: this.roll, angleThreshold: this.angleThreshold), + child: Padding( + padding: EdgeInsets.all(10), + child: ClipOval( + child: Container( + color: roll.abs() > _MAX_ROLL + ? Colors.red.withOpacity(0.5) + : Colors.transparent, + child: Stack(children: [ + Image.asset(this.neckAssetPath), + Transform.rotate( + angle: this.roll.isFinite + ? roll.abs() < _MAX_ROLL + ? this.roll + : roll.sign * _MAX_ROLL + : 0, + alignment: this.headAlignment, + child: Image.asset(this.headAssetPath)), + ]))))), ]); } -} \ No newline at end of file +} diff --git a/open_earable/lib/apps/posture_tracker/view/posture_tracker_view.dart b/open_earable/lib/apps/posture_tracker/view/posture_tracker_view.dart index 21490f3..d66b0bc 100644 --- a/open_earable/lib/apps/posture_tracker/view/posture_tracker_view.dart +++ b/open_earable/lib/apps/posture_tracker/view/posture_tracker_view.dart @@ -10,7 +10,6 @@ import 'package:provider/provider.dart'; import 'package:open_earable_flutter/src/open_earable_flutter.dart'; - class PostureTrackerView extends StatefulWidget { final AttitudeTracker _tracker; final OpenEarable _openEarable; @@ -25,41 +24,41 @@ class _PostureTrackerViewState extends State { @override Widget build(BuildContext context) { return ChangeNotifierProvider( - create: (context) => PostureTrackerViewModel(widget._tracker, BadPostureReminder(widget._openEarable, widget._tracker)), + create: (context) => PostureTrackerViewModel(widget._tracker, + BadPostureReminder(widget._openEarable, widget._tracker)), builder: (context, child) => Consumer( - builder: (context, postureTrackerViewModel, child) => Scaffold( - appBar: AppBar( - title: const Text("Posture Tracker"), - actions: [ - IconButton( - onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => SettingsView(postureTrackerViewModel))), - icon: Icon(Icons.settings) - ), - ], - ), - body: Center( - child: this._buildContentView(postureTrackerViewModel), - ), - ) - ) - ); + builder: (context, postureTrackerViewModel, child) => Scaffold( + appBar: AppBar( + title: const Text("Posture Tracker"), + actions: [ + IconButton( + onPressed: () => Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + SettingsView(postureTrackerViewModel))), + icon: Icon(Icons.settings)), + ], + ), + body: Center( + child: this._buildContentView(postureTrackerViewModel), + ), + backgroundColor: Theme.of(context).colorScheme.background, + ))); } Widget _buildContentView(PostureTrackerViewModel postureTrackerViewModel) { var headViews = this._createHeadViews(postureTrackerViewModel); - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ...headViews.map((e) => FractionallySizedBox( - widthFactor: .7, - child: e, - )), - this._buildTrackingButton(postureTrackerViewModel), - ] - ); + return Column(mainAxisAlignment: MainAxisAlignment.center, children: [ + ...headViews.map((e) => FractionallySizedBox( + widthFactor: .7, + child: e, + )), + this._buildTrackingButton(postureTrackerViewModel), + ]); } - Widget _buildHeadView(String headAssetPath, String neckAssetPath, AlignmentGeometry headAlignment, double roll, double angleThreshold) { + Widget _buildHeadView(String headAssetPath, String neckAssetPath, + AlignmentGeometry headAlignment, double roll, double angleThreshold) { return Padding( padding: const EdgeInsets.all(5), child: PostureRollView( @@ -75,33 +74,41 @@ class _PostureTrackerViewState extends State { List _createHeadViews(postureTrackerViewModel) { return [ this._buildHeadView( - "assets/posture_tracker/Head_Front.png", - "assets/posture_tracker/Neck_Front.png", - Alignment.center.add(Alignment(0, 0.3)), - postureTrackerViewModel.attitude.roll, - postureTrackerViewModel.badPostureSettings.rollAngleThreshold.toDouble() - ), + "assets/posture_tracker/Head_Front.png", + "assets/posture_tracker/Neck_Front.png", + Alignment.center.add(Alignment(0, 0.3)), + postureTrackerViewModel.attitude.roll, + postureTrackerViewModel.badPostureSettings.rollAngleThreshold + .toDouble()), this._buildHeadView( - "assets/posture_tracker/Head_Side.png", - "assets/posture_tracker/Neck_Side.png", - Alignment.center.add(Alignment(0, 0.3)), - -postureTrackerViewModel.attitude.pitch, - postureTrackerViewModel.badPostureSettings.pitchAngleThreshold.toDouble() - ), + "assets/posture_tracker/Head_Side.png", + "assets/posture_tracker/Neck_Side.png", + Alignment.center.add(Alignment(0, 0.3)), + -postureTrackerViewModel.attitude.pitch, + postureTrackerViewModel.badPostureSettings.pitchAngleThreshold + .toDouble()), ]; } - + Widget _buildTrackingButton(PostureTrackerViewModel postureTrackerViewModel) { return Column(children: [ ElevatedButton( onPressed: postureTrackerViewModel.isAvailable - ? () { postureTrackerViewModel.isTracking ? postureTrackerViewModel.stopTracking() : postureTrackerViewModel.startTracking(); } - : null, + ? () { + postureTrackerViewModel.isTracking + ? postureTrackerViewModel.stopTracking() + : postureTrackerViewModel.startTracking(); + } + : null, style: ElevatedButton.styleFrom( - backgroundColor: !postureTrackerViewModel.isTracking ? Color(0xff77F2A1) : Color(0xfff27777), - foregroundColor: Colors.black, - ), - child: postureTrackerViewModel.isTracking ? const Text("Stop Tracking") : const Text("Start Tracking"), + backgroundColor: !postureTrackerViewModel.isTracking + ? Color(0xff77F2A1) + : Color(0xfff27777), + foregroundColor: Colors.black, + ), + child: postureTrackerViewModel.isTracking + ? const Text("Stop Tracking") + : const Text("Start Tracking"), ), Visibility( visible: !postureTrackerViewModel.isAvailable, diff --git a/open_earable/lib/apps/posture_tracker/view/settings_view.dart b/open_earable/lib/apps/posture_tracker/view/settings_view.dart index 9dd1167..9076399 100644 --- a/open_earable/lib/apps/posture_tracker/view/settings_view.dart +++ b/open_earable/lib/apps/posture_tracker/view/settings_view.dart @@ -5,7 +5,7 @@ import 'package:provider/provider.dart'; class SettingsView extends StatefulWidget { final PostureTrackerViewModel _viewModel; - + SettingsView(this._viewModel); @override @@ -24,24 +24,27 @@ class _SettingsViewState extends State { void initState() { super.initState(); _viewModel = widget._viewModel; - _rollAngleThresholdController = TextEditingController(text: _viewModel.badPostureSettings.rollAngleThreshold.toString()); - _pitchAngleThresholdController = TextEditingController(text: _viewModel.badPostureSettings.pitchAngleThreshold.toString()); - _badPostureTimeThresholdController = TextEditingController(text: _viewModel.badPostureSettings.timeThreshold.toString()); - _goodPostureTimeThresholdController = TextEditingController(text: _viewModel.badPostureSettings.resetTimeThreshold.toString()); + _rollAngleThresholdController = TextEditingController( + text: _viewModel.badPostureSettings.rollAngleThreshold.toString()); + _pitchAngleThresholdController = TextEditingController( + text: _viewModel.badPostureSettings.pitchAngleThreshold.toString()); + _badPostureTimeThresholdController = TextEditingController( + text: _viewModel.badPostureSettings.timeThreshold.toString()); + _goodPostureTimeThresholdController = TextEditingController( + text: _viewModel.badPostureSettings.resetTimeThreshold.toString()); } @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text("Posture Tracker Settings") - ), + appBar: AppBar(title: const Text("Posture Tracker Settings")), body: ChangeNotifierProvider.value( - value: _viewModel, - builder: (context, child) => Consumer( - builder: (context, postureTrackerViewModel, child) => _buildSettingsView(), - ) - ), + value: _viewModel, + builder: (context, child) => Consumer( + builder: (context, postureTrackerViewModel, child) => + _buildSettingsView(), + )), + backgroundColor: Theme.of(context).colorScheme.background, ); } @@ -52,13 +55,16 @@ class _SettingsViewState extends State { color: Theme.of(context).colorScheme.primary, child: ListTile( title: Text("Status"), - trailing: Text(_viewModel.isTracking ? "Tracking" : _viewModel.isAvailable ? "Available" : "Unavailable"), + trailing: Text(_viewModel.isTracking + ? "Tracking" + : _viewModel.isAvailable + ? "Available" + : "Unavailable"), ), ), Card( - color: Theme.of(context).colorScheme.primary, - child: Column( - children: [ + color: Theme.of(context).colorScheme.primary, + child: Column(children: [ // add a switch to control the `isActive` property of the `BadPostureSettings` SwitchListTile( title: Text("Bad Posture Reminder"), @@ -70,8 +76,7 @@ class _SettingsViewState extends State { }, ), Visibility( - child: Column( - children: [ + child: Column(children: [ ListTile( title: Text("Roll Angle Threshold (in degrees)"), trailing: SizedBox( @@ -82,17 +87,18 @@ class _SettingsViewState extends State { textAlign: TextAlign.end, style: TextStyle(color: Colors.black), decoration: InputDecoration( - contentPadding: EdgeInsets.all(10), - floatingLabelBehavior: - FloatingLabelBehavior.never, - border: OutlineInputBorder(), - labelText: 'Roll', - filled: true, - labelStyle: TextStyle(color: Colors.black), - fillColor: Colors.white - ), + contentPadding: EdgeInsets.all(10), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: OutlineInputBorder(), + labelText: 'Roll', + filled: true, + labelStyle: TextStyle(color: Colors.black), + fillColor: Colors.white), keyboardType: TextInputType.number, - onChanged: (_) { _updatePostureSettings(); }, + onChanged: (_) { + _updatePostureSettings(); + }, ), ), ), @@ -106,17 +112,18 @@ class _SettingsViewState extends State { textAlign: TextAlign.end, style: TextStyle(color: Colors.black), decoration: InputDecoration( - contentPadding: EdgeInsets.all(10), - floatingLabelBehavior: - FloatingLabelBehavior.never, - border: OutlineInputBorder(), - labelText: 'Pitch', - filled: true, - labelStyle: TextStyle(color: Colors.black), - fillColor: Colors.white - ), + contentPadding: EdgeInsets.all(10), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: OutlineInputBorder(), + labelText: 'Pitch', + filled: true, + labelStyle: TextStyle(color: Colors.black), + fillColor: Colors.white), keyboardType: TextInputType.number, - onChanged: (_) { _updatePostureSettings(); }, + onChanged: (_) { + _updatePostureSettings(); + }, ), ), ), @@ -130,17 +137,18 @@ class _SettingsViewState extends State { textAlign: TextAlign.end, style: TextStyle(color: Colors.black), decoration: InputDecoration( - contentPadding: EdgeInsets.all(10), - floatingLabelBehavior: - FloatingLabelBehavior.never, - border: OutlineInputBorder(), - labelText: 'Seconds', - filled: true, - labelStyle: TextStyle(color: Colors.black), - fillColor: Colors.white - ), + contentPadding: EdgeInsets.all(10), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: OutlineInputBorder(), + labelText: 'Seconds', + filled: true, + labelStyle: TextStyle(color: Colors.black), + fillColor: Colors.white), keyboardType: TextInputType.number, - onChanged: (_) { _updatePostureSettings(); }, + onChanged: (_) { + _updatePostureSettings(); + }, ), ), ), @@ -154,57 +162,47 @@ class _SettingsViewState extends State { textAlign: TextAlign.end, style: TextStyle(color: Colors.black), decoration: InputDecoration( - contentPadding: EdgeInsets.all(10), - floatingLabelBehavior: - FloatingLabelBehavior.never, - border: OutlineInputBorder(), - labelText: 'Seconds', - filled: true, - labelStyle: TextStyle(color: Colors.black), - fillColor: Colors.white - ), + contentPadding: EdgeInsets.all(10), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: OutlineInputBorder(), + labelText: 'Seconds', + filled: true, + labelStyle: TextStyle(color: Colors.black), + fillColor: Colors.white), keyboardType: TextInputType.number, - onChanged: (_) { _updatePostureSettings(); }, + onChanged: (_) { + _updatePostureSettings(); + }, ), ), ), - ] - ), - visible: _viewModel.badPostureSettings.isActive - ), - ] - ) - - ), + ]), + visible: _viewModel.badPostureSettings.isActive), + ])), Padding( padding: EdgeInsets.all(8.0), - child: Row( - children: [ - Expanded( - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: - _viewModel.isTracking + child: Row(children: [ + Expanded( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: _viewModel.isTracking ? Colors.green[300] : Colors.blue[300], - foregroundColor: Colors.black, - ), - onPressed: - _viewModel.isTracking + foregroundColor: Colors.black, + ), + onPressed: _viewModel.isTracking ? () { - _viewModel.calibrate(); - Navigator.of(context).pop(); - } + _viewModel.calibrate(); + Navigator.of(context).pop(); + } : () => _viewModel.startTracking(), - child: Text( - _viewModel.isTracking + child: Text(_viewModel.isTracking ? "Calibrate as Main Posture" - : "Start Calibration" - ), - ), - ) - ] - ), + : "Start Calibration"), + ), + ) + ]), ), ], ); @@ -213,15 +211,16 @@ class _SettingsViewState extends State { void _updatePostureSettings() { BadPostureSettings settings = _viewModel.badPostureSettings; settings.rollAngleThreshold = int.parse(_rollAngleThresholdController.text); - settings.pitchAngleThreshold = int.parse(_pitchAngleThresholdController.text); + settings.pitchAngleThreshold = + int.parse(_pitchAngleThresholdController.text); settings.timeThreshold = int.parse(_badPostureTimeThresholdController.text); - settings.resetTimeThreshold = int.parse(_goodPostureTimeThresholdController.text); + settings.resetTimeThreshold = + int.parse(_goodPostureTimeThresholdController.text); _viewModel.setBadPostureSettings(settings); } - @override void dispose() { super.dispose(); } -} \ No newline at end of file +} diff --git a/open_earable/lib/controls_tab/views/audio_player.dart b/open_earable/lib/controls_tab/views/audio_player.dart index 7674090..8d10cbb 100644 --- a/open_earable/lib/controls_tab/views/audio_player.dart +++ b/open_earable/lib/controls_tab/views/audio_player.dart @@ -184,7 +184,7 @@ class _AudioPlayerCardState extends State { padding: const EdgeInsets.symmetric(horizontal: 5.0), child: Card( //Audio Player Card - color: Color(0xff161618), + color: Theme.of(context).colorScheme.primary, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( diff --git a/open_earable/lib/controls_tab/views/connect.dart b/open_earable/lib/controls_tab/views/connect.dart index 47a572f..7da1668 100644 --- a/open_earable/lib/controls_tab/views/connect.dart +++ b/open_earable/lib/controls_tab/views/connect.dart @@ -13,7 +13,7 @@ class ConnectCard extends StatelessWidget { return Padding( padding: const EdgeInsets.symmetric(horizontal: 5.0), child: Card( - color: Color(0xff161618), + color: Theme.of(context).colorScheme.primary, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( diff --git a/open_earable/lib/controls_tab/views/led_color.dart b/open_earable/lib/controls_tab/views/led_color.dart index 42ec8ff..253d450 100644 --- a/open_earable/lib/controls_tab/views/led_color.dart +++ b/open_earable/lib/controls_tab/views/led_color.dart @@ -150,7 +150,7 @@ class _LEDColorCardState extends State { padding: const EdgeInsets.symmetric(horizontal: 5.0), child: Card( //LED Color Picker Card - color: Color(0xff161618), + color: Theme.of(context).colorScheme.primary, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( diff --git a/open_earable/lib/controls_tab/views/sensor_configuration.dart b/open_earable/lib/controls_tab/views/sensor_configuration.dart index c52c2f5..eee6266 100644 --- a/open_earable/lib/controls_tab/views/sensor_configuration.dart +++ b/open_earable/lib/controls_tab/views/sensor_configuration.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:open_earable_flutter/src/open_earable_flutter.dart'; import '../models/open_earable_settings.dart'; @@ -97,7 +98,7 @@ class _SensorConfigurationCardState extends State { padding: const EdgeInsets.symmetric(horizontal: 5.0), child: Card( //Audio Player Card - color: Color(0xff161618), + color: Theme.of(context).colorScheme.primary, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( @@ -132,9 +133,7 @@ class _SensorConfigurationCardState extends State { (bool? newValue) { if (newValue != null) { setState(() { - print("SELECTED $newValue"); OpenEarableSettings().barometerSettingSelected = newValue; - print(OpenEarableSettings().barometerSettingSelected); }); } }, (String newValue) { diff --git a/open_earable/lib/main.dart b/open_earable/lib/main.dart index 6aca296..bbc0762 100644 --- a/open_earable/lib/main.dart +++ b/open_earable/lib/main.dart @@ -20,13 +20,15 @@ class MyApp extends StatelessWidget { useMaterial3: false, colorScheme: ColorScheme( brightness: Brightness.dark, - primary: Color.fromARGB(255, 22, 22, 24), + primary: Color.fromARGB( + 255, 54, 53, 59), //Color.fromARGB(255, 22, 22, 24) onPrimary: Colors.white, secondary: Color.fromARGB(255, 119, 242, 161), onSecondary: Colors.white, error: Colors.red, onError: Colors.black, - background: Color.fromARGB(255, 54, 53, 59), + background: Color.fromARGB( + 255, 22, 22, 24), //Color.fromARGB(255, 54, 53, 59) onBackground: Colors.white, surface: Color.fromARGB(255, 22, 22, 24), onSurface: Colors.white), @@ -106,7 +108,7 @@ class _MyHomePageState extends State { ), body: _widgetOptions.elementAt(_selectedIndex), bottomNavigationBar: BottomNavigationBar( - backgroundColor: Theme.of(context).colorScheme.primary, + backgroundColor: Theme.of(context).colorScheme.background, items: const [ BottomNavigationBarItem( icon: Padding( From b01769100c8d99528c40c98ac5467dc22e8a85cf Mon Sep 17 00:00:00 2001 From: Philipp Lepold Date: Wed, 13 Dec 2023 11:04:24 +0100 Subject: [PATCH 10/15] fixes "invalid CFBundleShortVersionString" --- open_earable/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/open_earable/pubspec.yaml b/open_earable/pubspec.yaml index 80b0e37..0ccd287 100644 --- a/open_earable/pubspec.yaml +++ b/open_earable/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.1+1 +version: 1.0.2+1 environment: sdk: '>=3.0.6 <4.0.0' From e7b7f2cb8120b4bf46dea6c9c5e43803238c6477 Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Wed, 13 Dec 2023 15:36:21 +0100 Subject: [PATCH 11/15] add timer to recorder app, display warning if earable is not connected --- open_earable/assets/digital-7-mono.ttf | Bin 0 -> 34404 bytes open_earable/lib/apps/recorder.dart | 261 +++++++++++------- .../lib/sensor_data_tab/sensor_data_tab.dart | 34 +-- .../earable_not_connected_warning.dart | 35 +++ open_earable/pubspec.yaml | 4 +- 5 files changed, 198 insertions(+), 136 deletions(-) create mode 100644 open_earable/assets/digital-7-mono.ttf create mode 100644 open_earable/lib/widgets/earable_not_connected_warning.dart diff --git a/open_earable/assets/digital-7-mono.ttf b/open_earable/assets/digital-7-mono.ttf new file mode 100644 index 0000000000000000000000000000000000000000..74209e62c76e07f5663e4583a6a4e45e27b75491 GIT binary patch literal 34404 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 { late List _csvHeader; late List _labels; late String _selectedLabel; + Timer? _timer; + Duration _duration = Duration(); @override void initState() { super.initState(); + _labels = [ "No Label", "Label 1", @@ -63,9 +67,32 @@ class _RecorderState extends State { listFilesInDocumentsDirectory(); } + void _startTimer() { + _timer = Timer.periodic(Duration(seconds: 1), (timer) { + setState(() { + _duration = _duration + Duration(seconds: 1); + }); + }); + } + + void _stopTimer() { + if (_timer != null) { + _timer!.cancel(); + _duration = Duration(); + } + } + + String _formatDuration(Duration duration) { + String twoDigits(int n) => n.toString().padLeft(2, '0'); + String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); + String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); + return "$twoDigitMinutes:$twoDigitSeconds"; + } + @override void dispose() { super.dispose(); + _timer?.cancel(); _imuSubscription?.cancel(); _barometerSubscription?.cancel(); } @@ -178,9 +205,11 @@ class _RecorderState extends State { _recording = false; }); _csvWriter?.cancelTimer(); + _stopTimer(); } else { _csvWriter = CsvWriter(listFilesInDocumentsDirectory); _csvWriter?.addData(_csvHeader); + _startTimer(); setState(() { _recording = true; }); @@ -203,116 +232,142 @@ class _RecorderState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Theme.of(context).colorScheme.background, - appBar: AppBar( - title: Text('Recorder'), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - children: [ - Padding( - padding: EdgeInsets.all(16), - child: ElevatedButton( - onPressed: startStopRecording, - style: ElevatedButton.styleFrom( - backgroundColor: _recording - ? Color(0xfff27777) - : Theme.of(context).colorScheme.secondary, - foregroundColor: Colors.black, - ), - child: Text( - _recording ? "Stop Recording" : "Start Recording", - style: TextStyle(fontSize: 20), - ), + backgroundColor: Theme.of(context).colorScheme.background, + appBar: AppBar( + title: Text('Recorder'), + ), + body: _openEarable.bleManager.connected + ? _recorderWidget() + : EarableNotConnectedWarning()); + } + + Widget _recorderWidget() { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.fromLTRB(16, 16, 16, 0), + child: Text( + _formatDuration(_duration), + style: TextStyle( + fontFamily: 'Digital', // This is a common monospaced font + fontSize: 80, + fontWeight: FontWeight.normal, + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.all(16), + child: ElevatedButton( + onPressed: startStopRecording, + style: ElevatedButton.styleFrom( + minimumSize: Size(200, 36), + backgroundColor: _recording + ? Color(0xfff27777) + : Theme.of(context).colorScheme.secondary, + foregroundColor: Colors.black, + ), + child: Text( + _recording ? "Stop Recording" : "Start Recording", + style: TextStyle(fontSize: 20), ), ), - DropdownButton( - value: _selectedLabel, - icon: const Icon(Icons.arrow_drop_down), - onChanged: (String? newValue) { - setState(() { - _selectedLabel = newValue!; - }); - }, - items: _labels.map>((String value) { - return DropdownMenuItem( - value: value, - child: Text(value), - ); - }).toList(), - ), - ], - ), - Text("Recordings", style: TextStyle(fontSize: 20.0)), - Divider( - thickness: 2, - ), - Expanded( - child: _recordings.isEmpty - ? Stack( - fit: StackFit.expand, - children: [ - Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.warning, - size: 48, - color: Colors.red, - ), - SizedBox(height: 16), - Center( - child: Text( - "No recordings found", - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.center, + ), + DropdownButton( + value: _selectedLabel, + icon: const Icon(Icons.arrow_drop_down), + onChanged: (String? newValue) { + setState(() { + _selectedLabel = newValue!; + }); + }, + items: _labels.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ), + ], + ), + Text("Recordings", style: TextStyle(fontSize: 20.0)), + Divider( + thickness: 2, + ), + Expanded( + child: _recordings.isEmpty + ? Stack( + fit: StackFit.expand, + children: [ + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.warning, + size: 48, + color: Colors.red, + ), + SizedBox(height: 16), + Center( + child: Text( + "No recordings found", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, ), + textAlign: TextAlign.center, ), - ], - ), + ), + ], ), - ], - ) - : ListView.builder( - itemCount: _recordings.length, - itemBuilder: (context, index) { - return ListTile( - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: FittedBox( - fit: BoxFit.scaleDown, - child: Text( - _recordings[index].path.split("/").last, - maxLines: 1, - ), + ), + ], + ) + : ListView.builder( + itemCount: _recordings.length, + itemBuilder: (context, index) { + return ListTile( + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + _recordings[index].path.split("/").last, + maxLines: 1, ), ), - IconButton( - icon: Icon(Icons.delete), - onPressed: () { - deleteFile(_recordings[index]); - }, - ), - ], - ), - onTap: () { - OpenFile.open(_recordings[index].path); - }, - ); - }, - ), - ) - ], - ), + ), + IconButton( + icon: Icon(Icons.delete, + color: (_recording && index == 0) + ? Color.fromARGB(50, 255, 255, 255) + : Colors.white), + onPressed: () { + (_recording && index == 0) + ? null + : deleteFile(_recordings[index]); + }, + splashColor: (_recording && index == 0) + ? Colors.transparent + : Theme.of(context).splashColor, + ), + ], + ), + onTap: () { + OpenFile.open(_recordings[index].path); + }, + ); + }, + ), + ) + ], ), ); } diff --git a/open_earable/lib/sensor_data_tab/sensor_data_tab.dart b/open_earable/lib/sensor_data_tab/sensor_data_tab.dart index 836255b..b89b53e 100644 --- a/open_earable/lib/sensor_data_tab/sensor_data_tab.dart +++ b/open_earable/lib/sensor_data_tab/sensor_data_tab.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:open_earable/sensor_data_tab/earable_3d_model.dart'; +import 'package:open_earable/widgets/earable_not_connected_warning.dart'; import 'package:open_earable_flutter/src/open_earable_flutter.dart'; import 'package:open_earable/sensor_data_tab/sensor_chart.dart'; @@ -58,43 +59,12 @@ class _SensorDataTabState extends State @override Widget build(BuildContext context) { if (!_openEarable.bleManager.connected) { - return _notConnectedWidget(); + return EarableNotConnectedWarning(); } else { return _buildSensorDataTabs(); } } - Widget _notConnectedWidget() { - return Stack( - fit: StackFit.expand, - children: [ - Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.warning, - size: 48, - color: Colors.red, - ), - SizedBox(height: 16), - Center( - child: Text( - "Not connected to\nOpenEarable device", - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.center, - ), - ), - ], - ), - ), - ], - ); - } - Widget _buildSensorDataTabs() { return Scaffold( backgroundColor: Theme.of(context).colorScheme.background, diff --git a/open_earable/lib/widgets/earable_not_connected_warning.dart b/open_earable/lib/widgets/earable_not_connected_warning.dart new file mode 100644 index 0000000..2ac7d9a --- /dev/null +++ b/open_earable/lib/widgets/earable_not_connected_warning.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +class EarableNotConnectedWarning extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Stack( + fit: StackFit.expand, + children: [ + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.warning, + size: 48, + color: Colors.red, + ), + SizedBox(height: 16), + Center( + child: Text( + "Not connected to\nOpenEarable device", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + ), + ), + ], + ), + ), + ], + ); + } +} diff --git a/open_earable/pubspec.yaml b/open_earable/pubspec.yaml index 0ccd287..d65c0d1 100644 --- a/open_earable/pubspec.yaml +++ b/open_earable/pubspec.yaml @@ -128,7 +128,9 @@ flutter: - family: OpenEarableIcon fonts: - asset: assets/OpenEarableIcon.ttf - + - family: Digital + fonts: + - asset: assets/digital-7-mono.ttf # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware From 84b707756dda7b402946c1e4c0576f21c1a242ba Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Wed, 13 Dec 2023 15:52:03 +0100 Subject: [PATCH 12/15] disable radio selection for audio player when earable is not connected --- .../models/open_earable_settings.dart | 2 +- .../lib/controls_tab/views/audio_player.dart | 53 +++++++++---------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/open_earable/lib/controls_tab/models/open_earable_settings.dart b/open_earable/lib/controls_tab/models/open_earable_settings.dart index 72ba357..0e5a4a2 100644 --- a/open_earable/lib/controls_tab/models/open_earable_settings.dart +++ b/open_earable/lib/controls_tab/models/open_earable_settings.dart @@ -64,7 +64,7 @@ class OpenEarableSettings { selectedBarometerOption = "0"; selectedMicrophoneOption = "0"; - selectedAudioPlayerRadio = 0; + selectedAudioPlayerRadio = 3; // no radio is selected selectedJingle = jingleMap[1]!; selectedWaveForm = waveFormMap[1]!; selectedFilename = "filename.wav"; diff --git a/open_earable/lib/controls_tab/views/audio_player.dart b/open_earable/lib/controls_tab/views/audio_player.dart index 8d10cbb..2f84c3f 100644 --- a/open_earable/lib/controls_tab/views/audio_player.dart +++ b/open_earable/lib/controls_tab/views/audio_player.dart @@ -209,18 +209,33 @@ class _AudioPlayerCardState extends State { ); } + Widget _getAudioPlayerRadio(int index) { + return Radio( + value: index, + groupValue: OpenEarableSettings().selectedAudioPlayerRadio, + onChanged: !_openEarable.bleManager.connected + ? null + : (int? value) { + setState(() { + OpenEarableSettings().selectedAudioPlayerRadio = value ?? 0; + }); + }, + activeColor: !_openEarable.bleManager.connected + ? Colors.grey + : Theme.of(context).colorScheme.secondary, + fillColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return Colors.grey; + } + return Theme.of(context).colorScheme.secondary; + }), + ); + } + Widget _getFileNameRow() { return Row( children: [ - Radio( - value: 0, - groupValue: OpenEarableSettings().selectedAudioPlayerRadio, - onChanged: (int? value) { - setState(() { - OpenEarableSettings().selectedAudioPlayerRadio = value ?? 0; - }); - }, - ), + _getAudioPlayerRadio(0), Expanded( child: SizedBox( height: 37.0, @@ -255,15 +270,7 @@ class _AudioPlayerCardState extends State { Widget _getJingleRow() { return Row( children: [ - Radio( - value: 1, - groupValue: OpenEarableSettings().selectedAudioPlayerRadio, - onChanged: (int? value) { - setState(() { - OpenEarableSettings().selectedAudioPlayerRadio = value ?? 0; - }); - }, - ), + _getAudioPlayerRadio(1), Expanded( child: SizedBox( height: 37.0, @@ -300,15 +307,7 @@ class _AudioPlayerCardState extends State { Widget _getFrequencyRow() { return Row( children: [ - Radio( - value: 2, - groupValue: OpenEarableSettings().selectedAudioPlayerRadio, - onChanged: (int? value) { - setState(() { - OpenEarableSettings().selectedAudioPlayerRadio = value ?? 0; - }); - }, - ), + _getAudioPlayerRadio(2), SizedBox( height: 37.0, width: 75, From 3b7423d248322bdce039853c45b05f5b3a559df8 Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Mon, 18 Dec 2023 01:32:10 +0100 Subject: [PATCH 13/15] alert now shows and makes app unusable when bluetooth is turned off or permissions are not given --- open_earable/ios/Podfile.lock | 6 ++++ open_earable/lib/main.dart | 62 +++++++++++++++++++++++++++++++++-- open_earable/pubspec.lock | 46 +++++++++++++++++--------- open_earable/pubspec.yaml | 4 ++- 4 files changed, 100 insertions(+), 18 deletions(-) diff --git a/open_earable/ios/Podfile.lock b/open_earable/ios/Podfile.lock index 7d44463..fd2fbe4 100644 --- a/open_earable/ios/Podfile.lock +++ b/open_earable/ios/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - app_settings (5.1.1): + - Flutter - Flutter (1.0.0) - flutter_gl (0.0.3): - Flutter @@ -21,6 +23,7 @@ PODS: - three3d_egl (0.1.3) DEPENDENCIES: + - app_settings (from `.symlinks/plugins/app_settings/ios`) - Flutter (from `Flutter`) - flutter_gl (from `.symlinks/plugins/flutter_gl/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) @@ -36,6 +39,8 @@ SPEC REPOS: - three3d_egl EXTERNAL SOURCES: + app_settings: + :path: ".symlinks/plugins/app_settings/ios" Flutter: :path: Flutter flutter_gl: @@ -52,6 +57,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/reactive_ble_mobile/ios" SPEC CHECKSUMS: + app_settings: 017320c6a680cdc94c799949d95b84cb69389ebc Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_gl: 5a5603f35db897697f064027864a32b15d0c421d flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef diff --git a/open_earable/lib/main.dart b/open_earable/lib/main.dart index bbc0762..55f4f11 100644 --- a/open_earable/lib/main.dart +++ b/open_earable/lib/main.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:provider/provider.dart'; import 'package:flutter/material.dart'; import 'package:open_earable/open_earable_icon_icons.dart'; @@ -8,6 +10,8 @@ import 'apps_tab.dart'; import 'package:open_earable_flutter/src/open_earable_flutter.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:open_earable/controls_tab/models/open_earable_settings.dart'; +import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; +import 'package:app_settings/app_settings.dart'; void main() => runApp(MyApp()); @@ -46,12 +50,14 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { int _selectedIndex = 0; late OpenEarable _openEarable; - + final flutterReactiveBle = FlutterReactiveBle(); + late bool alertOpen; late List _widgetOptions; - + StreamSubscription? blePermissionSubscription; @override void initState() { super.initState(); + alertOpen = false; _checkBLEPermission(); _openEarable = OpenEarable(); _widgetOptions = [ @@ -61,6 +67,12 @@ class _MyHomePageState extends State { ]; } + @override + dispose() { + super.dispose(); + blePermissionSubscription?.cancel(); + } + Future _checkBLEPermission() async { PermissionStatus status = await Permission.bluetoothConnect.request(); PermissionStatus status2 = await Permission.location.request(); @@ -68,6 +80,52 @@ class _MyHomePageState extends State { if (status.isGranted) { print("BLE is working"); } + blePermissionSubscription = + flutterReactiveBle.statusStream.listen((status) { + if (status != BleStatus.ready && + status != BleStatus.unknown && + alertOpen == false) { + alertOpen = true; + _showBluetoothAlert(context); + } + }); + } + + void _showBluetoothAlert(BuildContext context) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return AlertDialog( + title: Text("Bluetooth disabled"), + content: Text( + "Please make sure your device's bluetooth and location services are turned on and this app has been granted permission to use them in the app's settings.\nThis alert can only be closed if these requirements are fulfilled."), + actions: [ + TextButton( + child: Text( + 'Open App Settings', + style: TextStyle( + color: Theme.of(context).colorScheme.onBackground), + ), + onPressed: () { + AppSettings.openAppSettings(); + }, + ), + TextButton( + child: Text('OK', + style: TextStyle( + color: Theme.of(context).colorScheme.onBackground)), + onPressed: () { + if (flutterReactiveBle.status == BleStatus.ready) { + alertOpen = false; + Navigator.of(context).pop(); + } + }, + ), + ], + ); + }, + ); } void _onItemTapped(int index) { diff --git a/open_earable/pubspec.lock b/open_earable/pubspec.lock index bac6f8b..1e6d936 100644 --- a/open_earable/pubspec.lock +++ b/open_earable/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + app_settings: + dependency: "direct main" + description: + name: app_settings + sha256: "09bc7fe0313a507087bec1a3baf555f0576e816a760cbb31813a88890a09d9e5" + url: "https://pub.dev" + source: hosted + version: "5.1.1" archive: dependency: transitive description: @@ -232,7 +240,7 @@ packages: source: hosted version: "2.2.16" flutter_reactive_ble: - dependency: transitive + dependency: "direct main" description: name: flutter_reactive_ble sha256: "7a0d245412dc8e1b72ce2adc423808583b42ce824b1be74001ff22c8bb5ada48" @@ -350,10 +358,10 @@ packages: description: path: "." ref: HEAD - resolved-ref: eaf50163a03f24822f087a822c767c284f5b3637 + resolved-ref: "6841c0b704dffbe559961ea80dccf73abf0c293a" url: "https://github.com/OpenEarable/open_earable_flutter.git" source: git - version: "0.0.1" + version: "0.0.2" open_file: dependency: "direct main" description: @@ -427,45 +435,53 @@ packages: source: hosted version: "2.2.1" permission_handler: - dependency: transitive + dependency: "direct main" description: name: permission_handler - sha256: bc56bfe9d3f44c3c612d8d393bd9b174eb796d706759f9b495ac254e4294baa5 + sha256: "860c6b871c94c78e202dc69546d4d8fd84bd59faeb36f8fb9888668a53ff4f78" url: "https://pub.dev" source: hosted - version: "10.4.5" + version: "11.1.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "59c6322171c29df93a22d150ad95f3aa19ed86542eaec409ab2691b8f35f9a47" + sha256: "2f1bec180ee2f5665c22faada971a8f024761f632e93ddc23310487df52dcfa6" url: "https://pub.dev" source: hosted - version: "10.3.6" + version: "12.0.1" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" + sha256: "1a816084338ada8d574b1cb48390e6e8b19305d5120fe3a37c98825bacc78306" + url: "https://pub.dev" + source: hosted + version: "9.2.0" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "11b762a8c123dced6461933a88ea1edbbe036078c3f9f41b08886e678e7864df" url: "https://pub.dev" source: hosted - version: "9.1.4" + version: "0.1.0+2" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" + sha256: d87349312f7eaf6ce0adaf668daf700ac5b06af84338bd8b8574dfbd93ffe1a1 url: "https://pub.dev" source: hosted - version: "3.12.0" + version: "4.0.2" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 + sha256: "1e8640c1e39121128da6b816d236e714d2cf17fac5a105dd6acdd3403a628004" url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.2.0" petitparser: dependency: transitive description: @@ -691,4 +707,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.2.0-194.0.dev <4.0.0" - flutter: ">=3.7.0" + flutter: ">=3.16.0" diff --git a/open_earable/pubspec.yaml b/open_earable/pubspec.yaml index d65c0d1..f709a98 100644 --- a/open_earable/pubspec.yaml +++ b/open_earable/pubspec.yaml @@ -33,7 +33,9 @@ dependencies: open_earable_flutter: git: url: https://github.com/OpenEarable/open_earable_flutter.git - + permission_handler: ^11.1.0 + flutter_reactive_ble: ^5.2.0 + app_settings: ^5.1.1 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. From ad2973ef776645fb16d93486109a39223d080b26 Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Mon, 18 Dec 2023 01:44:00 +0100 Subject: [PATCH 14/15] fix radio colors in audio player --- open_earable/lib/controls_tab/views/audio_player.dart | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/open_earable/lib/controls_tab/views/audio_player.dart b/open_earable/lib/controls_tab/views/audio_player.dart index 2f84c3f..f5224b4 100644 --- a/open_earable/lib/controls_tab/views/audio_player.dart +++ b/open_earable/lib/controls_tab/views/audio_player.dart @@ -220,14 +220,11 @@ class _AudioPlayerCardState extends State { OpenEarableSettings().selectedAudioPlayerRadio = value ?? 0; }); }, - activeColor: !_openEarable.bleManager.connected - ? Colors.grey - : Theme.of(context).colorScheme.secondary, fillColor: MaterialStateProperty.resolveWith((states) { - if (states.contains(MaterialState.disabled)) { - return Colors.grey; + if (states.contains(MaterialState.selected)) { + return Theme.of(context).colorScheme.secondary; } - return Theme.of(context).colorScheme.secondary; + return Colors.grey; }), ); } From fa468af10a6c361560f6c1df8ebf0415f27a1ead Mon Sep 17 00:00:00 2001 From: Tobias Roeddiger Date: Sat, 23 Dec 2023 18:35:04 +0100 Subject: [PATCH 15/15] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba1986a..a614ab2 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

- ⬇️ Download Android app + ⬇️ Download Android beta app