Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions open_wearable/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
<!-- internet persmission, for fetching firmware update -->
<uses-permission android:name="android.permission.INTERNET"/>

<uses-permission android:name="android.permission.RECORD_AUDIO" />

<!-- Provide required visibility configuration for API level 30 and above -->
<queries>
<!-- If your app checks for SMS support -->
Expand Down
4 changes: 1 addition & 3 deletions open_wearable/ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,5 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>13.0</string>
</dict>
</plist>
</plist>
12 changes: 12 additions & 0 deletions open_wearable/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ PODS:
- flutter_archive (0.0.1):
- Flutter
- ZIPFoundation (= 0.9.19)
- flutter_headset_detector (3.1.0):
- Flutter
- iOSMcuManagerLibrary (1.10.1):
- SwiftCBOR (= 0.4.7)
- ZIPFoundation (= 0.9.19)
Expand All @@ -53,6 +55,8 @@ PODS:
- FlutterMacOS
- permission_handler_apple (9.3.0):
- Flutter
- record_ios (1.1.0):
- Flutter
- SDWebImage (5.21.3):
- SDWebImage/Core (= 5.21.3)
- SDWebImage/Core (5.21.3)
Expand All @@ -76,10 +80,12 @@ DEPENDENCIES:
- file_selector_ios (from `.symlinks/plugins/file_selector_ios/ios`)
- Flutter (from `Flutter`)
- flutter_archive (from `.symlinks/plugins/flutter_archive/ios`)
- flutter_headset_detector (from `.symlinks/plugins/flutter_headset_detector/ios`)
- mcumgr_flutter (from `.symlinks/plugins/mcumgr_flutter/ios`)
- open_file_ios (from `.symlinks/plugins/open_file_ios/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- record_ios (from `.symlinks/plugins/record_ios/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- universal_ble (from `.symlinks/plugins/universal_ble/darwin`)
Expand All @@ -105,6 +111,8 @@ EXTERNAL SOURCES:
:path: Flutter
flutter_archive:
:path: ".symlinks/plugins/flutter_archive/ios"
flutter_headset_detector:
:path: ".symlinks/plugins/flutter_headset_detector/ios"
mcumgr_flutter:
:path: ".symlinks/plugins/mcumgr_flutter/ios"
open_file_ios:
Expand All @@ -113,6 +121,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
record_ios:
:path: ".symlinks/plugins/record_ios/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
Expand All @@ -129,11 +139,13 @@ SPEC CHECKSUMS:
file_selector_ios: ec57ec07954363dd730b642e765e58f199bb621a
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_archive: ad8edfd7f7d1bb12058d05424ba93e27d9930efe
flutter_headset_detector: 37d2407c6c59aa6e8a9daecf732854862ff6dd4a
iOSMcuManagerLibrary: e9555825af11a61744fe369c12e1e66621061b58
mcumgr_flutter: 969e99cc15e9fe658242669ce1075bf4612aef8a
open_file_ios: 5ff7526df64e4394b4fe207636b67a95e83078bb
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
record_ios: f75fa1d57f840012775c0e93a38a7f3ceea1a374
SDWebImage: 16309af6d214ba3f77a7c6f6fdda888cb313a50a
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
Expand Down
82 changes: 82 additions & 0 deletions open_wearable/lib/apps/audio_recorder/model/audio_recorder.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:playback_capture/data/playback_capture_result.dart';
import 'package:playback_capture/playback_capture.dart';
import 'package:permission_handler/permission_handler.dart';

class AudioRecorder extends ChangeNotifier {
final PlaybackCapture _playbackCapture = PlaybackCapture();

bool _isRecording = false;
String _errorMessage = '';
List<Uint8List> _audioBuffers = [];

bool get isRecording => _isRecording;
String get errorMessage => _errorMessage;
List<Uint8List> get audioBuffers => _audioBuffers;

/// Start capturing system audio
Future<bool> startRecording() async {
_errorMessage = '';
notifyListeners();

final PlaybackCaptureResult result = await _playbackCapture.listenAudio(
audioDataCallback: (Uint8List data) {
_audioBuffers.add(data);
notifyListeners();
},
);

if (result != PlaybackCaptureResult.recording) {
if (result == PlaybackCaptureResult.missingAudioRecordPermission) {
// Request microphone permission
final status = await Permission.microphone.request();
if (status.isGranted) {
// Retry after permission granted
return await startRecording();
} else {
_errorMessage = 'Microphone permission denied';
notifyListeners();
return false;
}
} else if (result == PlaybackCaptureResult.recordRequestDenied) {
_errorMessage = 'Audio capture request denied by user';
notifyListeners();
return false;
} else {
_errorMessage = 'Failed to start recording: $result';
notifyListeners();
return false;
}
}

// Recording successfully started
_isRecording = true;
notifyListeners();
return true;
}

/// Stop capturing audio
Future<void> stopRecording() async {
//await _playbackCapture.stopCapture();
_isRecording = false;
notifyListeners();
}

/// Clear recorded audio buffers
void clearBuffers() {
_audioBuffers.clear();
notifyListeners();
}

/// Get total recorded data size in bytes
int get totalRecordedBytes {
return _audioBuffers.fold(0, (sum, buffer) => sum + buffer.length);
}

@override
void dispose() {
//_playbackCapture.stopCapture();
super.dispose();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:playback_capture/playback_capture.dart';

class AudioRecorderView extends StatefulWidget {
const AudioRecorderView({super.key});

@override
State<AudioRecorderView> createState() => _AudioRecorderViewState();
}

class _AudioRecorderViewState extends State<AudioRecorderView> {
bool _isRecording = false;

@override
Widget build(BuildContext context) {
return PlatformScaffold(
appBar: PlatformAppBar(
title: PlatformText("Audio Recorder"),
),
body: Center(
child: PlatformElevatedButton(
child:
PlatformText(_isRecording ? "Stop Recording" : "Start Recording"),
onPressed: () => (), //_toggleRecording,
),
),
);
}

// Recording logic here...
}
44 changes: 30 additions & 14 deletions open_wearable/lib/apps/widgets/apps_page.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:go_router/go_router.dart';
import 'package:open_earable_flutter/open_earable_flutter.dart';
import 'package:open_wearable/apps/audio_recorder/view/audio_recorder_view.dart';
import 'package:open_wearable/apps/heart_tracker/widgets/heart_tracker_page.dart';
import 'package:open_wearable/apps/posture_tracker/model/earable_attitude_tracker.dart';
import 'package:open_wearable/apps/posture_tracker/view/posture_tracker_view.dart';
import 'package:open_wearable/apps/widgets/select_earable_view.dart';
import 'package:open_wearable/apps/widgets/app_tile.dart';


class AppInfo {
final String logoPath;
final String title;
Expand All @@ -29,15 +31,17 @@ List<AppInfo> _apps = [
logoPath: "lib/apps/posture_tracker/assets/logo.png",
title: "Posture Tracker",
description: "Get feedback on bad posture",
widget: SelectEarableView(startApp: (wearable, sensorConfigProvider) {
return PostureTrackerView(
EarableAttitudeTracker(
wearable.requireCapability<SensorManager>(),
sensorConfigProvider,
wearable.name.endsWith("L"),
),
);
},),
widget: SelectEarableView(
startApp: (wearable, sensorConfigProvider) {
return PostureTrackerView(
EarableAttitudeTracker(
wearable.requireCapability<SensorManager>(),
sensorConfigProvider,
wearable.name.endsWith("L"),
),
);
},
),
),
AppInfo(
logoPath: "lib/apps/heart_tracker/assets/logo.png",
Expand All @@ -47,9 +51,12 @@ List<AppInfo> _apps = [
startApp: (wearable, _) {
if (wearable.hasCapability<SensorManager>()) {
//TODO: show alert if no ppg sensor is found
Sensor ppgSensor = wearable.requireCapability<SensorManager>().sensors.firstWhere(
(s) => s.sensorName.toLowerCase() == "photoplethysmography".toLowerCase(),
);
Sensor ppgSensor =
wearable.requireCapability<SensorManager>().sensors.firstWhere(
(s) =>
s.sensorName.toLowerCase() ==
"photoplethysmography".toLowerCase(),
);

return HeartTrackerPage(ppgSensor: ppgSensor);
}
Expand All @@ -64,6 +71,15 @@ List<AppInfo> _apps = [
},
),
),
/*
if (Platform.isAndroid)
AppInfo(
logoPath: "lib/apps/audio_recorder/assets/logo.png",
title: "Audio Recorder",
description: "Record system audio and Bluetooth streams",
widget: const AudioRecorderView(), // Your audio recorder page
),
*/
];

class AppsPage extends StatelessWidget {
Expand All @@ -75,7 +91,7 @@ class AppsPage extends StatelessWidget {
appBar: PlatformAppBar(
title: PlatformText("Apps"),
trailingActions: [
PlatformIconButton(
PlatformIconButton(
icon: Icon(context.platformIcons.bluetooth),
onPressed: () {
context.push('/connect-devices');
Expand Down
Loading
Loading