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)
+
-
[Get OpenEarable device now!](https://forms.gle/R3LMcqtyKwVH7PZB9)
+
## 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.
-
+
-
+
## 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