neoagent 2.3.1-beta.2 → 2.3.1-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/.env.example +39 -0
  2. package/README.md +2 -0
  3. package/docs/capabilities.md +2 -2
  4. package/docs/configuration.md +13 -5
  5. package/docs/integrations.md +4 -1
  6. package/flutter_app/.metadata +42 -0
  7. package/flutter_app/README.md +21 -0
  8. package/flutter_app/analysis_options.yaml +32 -0
  9. package/flutter_app/android/app/build.gradle.kts +109 -0
  10. package/flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
  11. package/flutter_app/android/app/src/main/AndroidManifest.xml +147 -0
  12. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +747 -0
  13. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthConnectGateway.kt +280 -0
  14. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncNotifications.kt +113 -0
  15. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncPayload.kt +57 -0
  16. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncScheduler.kt +78 -0
  17. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncWorker.kt +253 -0
  18. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/PermissionsRationaleActivity.kt +46 -0
  19. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingBootReceiver.kt +21 -0
  20. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingForegroundService.kt +586 -0
  21. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingStateStore.kt +78 -0
  22. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingUploadClient.kt +104 -0
  23. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiHomeWidgetProvider.kt +457 -0
  24. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiWidgetStore.kt +194 -0
  25. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/VoiceLaunchWidgetProvider.kt +67 -0
  26. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetConfigActivity.kt +228 -0
  27. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncScheduler.kt +72 -0
  28. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncWorker.kt +186 -0
  29. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetTaskRunWorker.kt +210 -0
  30. package/flutter_app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  31. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_bg.xml +11 -0
  32. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_task_bg.xml +8 -0
  33. package/flutter_app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  34. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget.xml +138 -0
  35. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget_task_row.xml +52 -0
  36. package/flutter_app/android/app/src/main/res/layout/neoagent_voice_widget.xml +49 -0
  37. package/flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  38. package/flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  39. package/flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  40. package/flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  41. package/flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  42. package/flutter_app/android/app/src/main/res/values/strings.xml +12 -0
  43. package/flutter_app/android/app/src/main/res/values/styles.xml +18 -0
  44. package/flutter_app/android/app/src/main/res/values-night/styles.xml +18 -0
  45. package/flutter_app/android/app/src/main/res/xml/file_paths.xml +6 -0
  46. package/flutter_app/android/app/src/main/res/xml/neoagent_ai_widget_info.xml +12 -0
  47. package/flutter_app/android/app/src/main/res/xml/neoagent_voice_widget_info.xml +12 -0
  48. package/flutter_app/android/app/src/profile/AndroidManifest.xml +7 -0
  49. package/flutter_app/android/build.gradle.kts +24 -0
  50. package/flutter_app/android/ci-release.keystore +0 -0
  51. package/flutter_app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  52. package/flutter_app/android/gradle.properties +3 -0
  53. package/flutter_app/android/key.properties +4 -0
  54. package/flutter_app/android/settings.gradle.kts +26 -0
  55. package/flutter_app/assets/branding/app_icon_1024.png +0 -0
  56. package/flutter_app/assets/branding/app_icon_128.png +0 -0
  57. package/flutter_app/assets/branding/app_icon_192.png +0 -0
  58. package/flutter_app/assets/branding/app_icon_256.png +0 -0
  59. package/flutter_app/assets/branding/app_icon_32.png +0 -0
  60. package/flutter_app/assets/branding/app_icon_512.png +0 -0
  61. package/flutter_app/assets/branding/app_icon_64.png +0 -0
  62. package/flutter_app/assets/branding/tray_icon_template.png +0 -0
  63. package/flutter_app/lib/features/location/location_service.dart +119 -0
  64. package/flutter_app/lib/features/notifications/notification_interceptor.dart +97 -0
  65. package/flutter_app/lib/main.dart +23057 -0
  66. package/flutter_app/lib/main_app_shell.dart +1682 -0
  67. package/flutter_app/lib/main_integrations.dart +931 -0
  68. package/flutter_app/lib/main_launcher.dart +959 -0
  69. package/flutter_app/lib/main_launcher_entry.dart +5 -0
  70. package/flutter_app/lib/main_models.dart +3473 -0
  71. package/flutter_app/lib/main_shared.dart +2861 -0
  72. package/flutter_app/lib/main_theme.dart +204 -0
  73. package/flutter_app/lib/main_voice_assistant.dart +831 -0
  74. package/flutter_app/lib/src/android_apk_drop_zone.dart +32 -0
  75. package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +16 -0
  76. package/flutter_app/lib/src/android_apk_drop_zone_web.dart +348 -0
  77. package/flutter_app/lib/src/android_app_installer.dart +22 -0
  78. package/flutter_app/lib/src/android_app_installer_io.dart +122 -0
  79. package/flutter_app/lib/src/android_app_installer_stub.dart +21 -0
  80. package/flutter_app/lib/src/android_launcher_bridge.dart +239 -0
  81. package/flutter_app/lib/src/app_launch_bridge.dart +29 -0
  82. package/flutter_app/lib/src/app_release_updater.dart +511 -0
  83. package/flutter_app/lib/src/backend_client.dart +1833 -0
  84. package/flutter_app/lib/src/desktop_companion.dart +2 -0
  85. package/flutter_app/lib/src/desktop_companion_actions.dart +586 -0
  86. package/flutter_app/lib/src/desktop_companion_io.dart +538 -0
  87. package/flutter_app/lib/src/desktop_companion_stub.dart +59 -0
  88. package/flutter_app/lib/src/desktop_native_bridge.dart +91 -0
  89. package/flutter_app/lib/src/desktop_screen_capture.dart +21 -0
  90. package/flutter_app/lib/src/desktop_screen_capture_io.dart +142 -0
  91. package/flutter_app/lib/src/desktop_screen_capture_stub.dart +12 -0
  92. package/flutter_app/lib/src/diagnostics_logger.dart +119 -0
  93. package/flutter_app/lib/src/health_bridge.dart +136 -0
  94. package/flutter_app/lib/src/live_voice_capture.dart +85 -0
  95. package/flutter_app/lib/src/messaging_access_summary.dart +46 -0
  96. package/flutter_app/lib/src/network/app_http_client.dart +53 -0
  97. package/flutter_app/lib/src/network/app_http_client_factory.dart +6 -0
  98. package/flutter_app/lib/src/network/app_http_client_io.dart +138 -0
  99. package/flutter_app/lib/src/network/app_http_client_stub.dart +3 -0
  100. package/flutter_app/lib/src/network/app_http_client_web.dart +94 -0
  101. package/flutter_app/lib/src/oauth_launcher.dart +33 -0
  102. package/flutter_app/lib/src/oauth_launcher_io.dart +77 -0
  103. package/flutter_app/lib/src/oauth_launcher_stub.dart +33 -0
  104. package/flutter_app/lib/src/oauth_launcher_web.dart +107 -0
  105. package/flutter_app/lib/src/recording_bridge.dart +232 -0
  106. package/flutter_app/lib/src/recording_bridge_io.dart +1019 -0
  107. package/flutter_app/lib/src/recording_bridge_stub.dart +120 -0
  108. package/flutter_app/lib/src/recording_bridge_web.dart +689 -0
  109. package/flutter_app/lib/src/recording_payloads.dart +86 -0
  110. package/flutter_app/lib/src/theme/palette.dart +81 -0
  111. package/flutter_app/lib/src/widget_bridge.dart +49 -0
  112. package/flutter_app/linux/CMakeLists.txt +128 -0
  113. package/flutter_app/linux/flutter/CMakeLists.txt +88 -0
  114. package/flutter_app/linux/flutter/generated_plugin_registrant.cc +43 -0
  115. package/flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
  116. package/flutter_app/linux/flutter/generated_plugins.cmake +31 -0
  117. package/flutter_app/linux/runner/CMakeLists.txt +26 -0
  118. package/flutter_app/linux/runner/main.cc +6 -0
  119. package/flutter_app/linux/runner/my_application.cc +144 -0
  120. package/flutter_app/linux/runner/my_application.h +18 -0
  121. package/flutter_app/linux/runner/resources/app_icon.png +0 -0
  122. package/flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
  123. package/flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
  124. package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +40 -0
  125. package/flutter_app/macos/Podfile +42 -0
  126. package/flutter_app/macos/Podfile.lock +87 -0
  127. package/flutter_app/macos/Runner/AppDelegate.swift +576 -0
  128. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  129. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  130. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  131. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  132. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  133. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  134. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  135. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  136. package/flutter_app/macos/Runner/Base.lproj/MainMenu.xib +342 -0
  137. package/flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
  138. package/flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
  139. package/flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
  140. package/flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
  141. package/flutter_app/macos/Runner/DebugProfile.entitlements +16 -0
  142. package/flutter_app/macos/Runner/Info.plist +36 -0
  143. package/flutter_app/macos/Runner/MainFlutterWindow.swift +19 -0
  144. package/flutter_app/macos/Runner/Release.entitlements +12 -0
  145. package/flutter_app/macos/Runner.xcodeproj/project.pbxproj +801 -0
  146. package/flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  147. package/flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
  148. package/flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +10 -0
  149. package/flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  150. package/flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
  151. package/flutter_app/patch_strings.py +12 -0
  152. package/flutter_app/pubspec.lock +1088 -0
  153. package/flutter_app/pubspec.yaml +53 -0
  154. package/flutter_app/test/messaging_access_summary_test.dart +22 -0
  155. package/flutter_app/test/recording_payloads_test.dart +53 -0
  156. package/flutter_app/third_party/desktop_audio_capture/LICENSE +21 -0
  157. package/flutter_app/third_party/desktop_audio_capture/README.md +262 -0
  158. package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +65 -0
  159. package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +153 -0
  160. package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +110 -0
  161. package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +461 -0
  162. package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +91 -0
  163. package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +106 -0
  164. package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +219 -0
  165. package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +336 -0
  166. package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +101 -0
  167. package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +692 -0
  168. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +35 -0
  169. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +36 -0
  170. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +32 -0
  171. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +32 -0
  172. package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +878 -0
  173. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +27 -0
  174. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +1172 -0
  175. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +655 -0
  176. package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +12 -0
  177. package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +30 -0
  178. package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +87 -0
  179. package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +105 -0
  180. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +80 -0
  181. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +31 -0
  182. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +12 -0
  183. package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +23 -0
  184. package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +25 -0
  185. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +1117 -0
  186. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +115 -0
  187. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +777 -0
  188. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +87 -0
  189. package/flutter_app/third_party/flutter_secure_storage_linux/linux/CMakeLists.txt +30 -0
  190. package/flutter_app/third_party/flutter_secure_storage_linux/linux/flutter_secure_storage_linux_plugin.cc +215 -0
  191. package/flutter_app/third_party/flutter_secure_storage_linux/linux/include/flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h +27 -0
  192. package/flutter_app/third_party/flutter_secure_storage_linux/pubspec.yaml +20 -0
  193. package/flutter_app/tool/generate_desktop_branding.py +219 -0
  194. package/flutter_app/web/favicon.png +0 -0
  195. package/flutter_app/web/favicon.svg +12 -0
  196. package/flutter_app/web/icons/Icon-192.png +0 -0
  197. package/flutter_app/web/icons/Icon-512.png +0 -0
  198. package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
  199. package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
  200. package/flutter_app/web/index.html +39 -0
  201. package/flutter_app/web/manifest.json +35 -0
  202. package/flutter_app/windows/CMakeLists.txt +108 -0
  203. package/flutter_app/windows/flutter/CMakeLists.txt +109 -0
  204. package/flutter_app/windows/flutter/generated_plugin_registrant.cc +47 -0
  205. package/flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
  206. package/flutter_app/windows/flutter/generated_plugins.cmake +35 -0
  207. package/flutter_app/windows/runner/CMakeLists.txt +41 -0
  208. package/flutter_app/windows/runner/Runner.rc +121 -0
  209. package/flutter_app/windows/runner/flutter_window.cpp +533 -0
  210. package/flutter_app/windows/runner/flutter_window.h +37 -0
  211. package/flutter_app/windows/runner/main.cpp +53 -0
  212. package/flutter_app/windows/runner/resource.h +16 -0
  213. package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
  214. package/flutter_app/windows/runner/runner.exe.manifest +14 -0
  215. package/flutter_app/windows/runner/utils.cpp +65 -0
  216. package/flutter_app/windows/runner/utils.h +19 -0
  217. package/flutter_app/windows/runner/win32_window.cpp +299 -0
  218. package/flutter_app/windows/runner/win32_window.h +102 -0
  219. package/lib/manager.js +231 -7
  220. package/package.json +3 -1
  221. package/server/db/database.js +68 -0
  222. package/server/http/middleware.js +50 -0
  223. package/server/http/routes.js +3 -1
  224. package/server/index.js +1 -0
  225. package/server/public/.last_build_id +1 -1
  226. package/server/public/assets/NOTICES +61 -0
  227. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  228. package/server/public/flutter_bootstrap.js +1 -1
  229. package/server/public/main.dart.js +65262 -64422
  230. package/server/routes/integrations.js +86 -0
  231. package/server/routes/memory.js +11 -2
  232. package/server/routes/screenHistory.js +46 -0
  233. package/server/routes/triggers.js +81 -0
  234. package/server/services/ai/models.js +30 -0
  235. package/server/services/ai/providers/githubCopilot.js +97 -0
  236. package/server/services/ai/providers/openai.js +2 -1
  237. package/server/services/ai/providers/openaiCodex.js +31 -0
  238. package/server/services/ai/settings.js +20 -0
  239. package/server/services/ai/systemPrompt.js +1 -1
  240. package/server/services/ai/tools.js +35 -6
  241. package/server/services/browser/controller.js +47 -3
  242. package/server/services/desktop/screenRecorder.js +172 -0
  243. package/server/services/integrations/env.js +5 -0
  244. package/server/services/integrations/github/common.js +106 -0
  245. package/server/services/integrations/github/provider.js +499 -0
  246. package/server/services/integrations/github/repos.js +1124 -0
  247. package/server/services/integrations/home_assistant/provider.js +306 -26
  248. package/server/services/integrations/manager.js +63 -7
  249. package/server/services/integrations/oauth_provider.js +13 -6
  250. package/server/services/integrations/provider_config_store.js +76 -0
  251. package/server/services/integrations/registry.js +4 -0
  252. package/server/services/integrations/trello/provider.js +744 -0
  253. package/server/services/integrations/whatsapp/provider.js +6 -2
  254. package/server/services/manager.js +22 -0
  255. package/server/services/memory/manager.js +39 -2
  256. package/server/services/skills/base_catalog.js +33 -0
  257. package/server/services/tasks/adapters/index.js +1 -0
  258. package/server/services/tasks/adapters/manual.js +12 -0
  259. package/server/services/tasks/runtime.js +1 -1
  260. package/server/services/voice/openaiClient.js +4 -1
  261. package/server/services/voice/providers.js +2 -1
  262. package/server/services/widgets/service.js +49 -4
  263. package/server/utils/local_secrets.js +56 -0
  264. package/server/utils/logger.js +37 -9
@@ -0,0 +1,110 @@
1
+ import 'package:desktop_audio_capture/audio_capture.dart';
2
+
3
+ /// Configuration class for system audio capture.
4
+ ///
5
+ /// This class allows you to configure audio capture parameters for system audio
6
+ /// (audio output from the device), such as sample rate and number of channels.
7
+ ///
8
+ /// Example:
9
+ /// ```dart
10
+ /// // Create with default values
11
+ /// final config = SystemAudioConfig();
12
+ ///
13
+ /// // Create with custom values
14
+ /// final customConfig = SystemAudioConfig(
15
+ /// sampleRate: 44100,
16
+ /// channels: 2,
17
+ /// );
18
+ ///
19
+ /// // Use with capture
20
+ /// final capture = SystemAudioCapture(config: customConfig);
21
+ ///
22
+ /// // Update configuration
23
+ /// final updatedConfig = customConfig.copyWith(
24
+ /// sampleRate: 48000,
25
+ /// );
26
+ /// capture.updateConfig(updatedConfig);
27
+ /// ```
28
+ class SystemAudioConfig extends AudioCaptureConfig {
29
+ /// Sample rate in Hz (default: 16000).
30
+ ///
31
+ /// Common values: 8000, 16000, 44100, 48000.
32
+ /// Higher sample rates provide better quality but use more resources.
33
+ final int sampleRate;
34
+
35
+ /// Number of audio channels (default: 1 for mono).
36
+ ///
37
+ /// - 1: Mono (single channel)
38
+ /// - 2: Stereo (two channels)
39
+ final int channels;
40
+
41
+ /// Creates a new [SystemAudioConfig] instance.
42
+ ///
43
+ /// All parameters are optional and have default values:
44
+ /// - [sampleRate]: 16000
45
+ /// - [channels]: 1
46
+ ///
47
+ /// Example:
48
+ /// ```dart
49
+ /// final config = SystemAudioConfig(
50
+ /// sampleRate: 44100,
51
+ /// channels: 2,
52
+ /// );
53
+ /// ```
54
+ SystemAudioConfig({
55
+ this.sampleRate = 16000,
56
+ this.channels = 1,
57
+ });
58
+
59
+ /// Creates a copy of this configuration with modified values.
60
+ ///
61
+ /// Only the specified parameters will be changed; others remain the same.
62
+ ///
63
+ /// Example:
64
+ /// ```dart
65
+ /// final config = SystemAudioConfig(
66
+ /// sampleRate: 16000,
67
+ /// channels: 1,
68
+ /// );
69
+ ///
70
+ /// // Change to stereo while keeping sample rate
71
+ /// final updated = config.copyWith(channels: 2);
72
+ /// // updated.sampleRate is still 16000
73
+ /// ```
74
+ SystemAudioConfig copyWith({
75
+ int? sampleRate,
76
+ int? channels,
77
+ }) {
78
+ return SystemAudioConfig(
79
+ sampleRate: sampleRate ?? this.sampleRate,
80
+ channels: channels ?? this.channels,
81
+ );
82
+ }
83
+
84
+ /// Converts this configuration to a map for method channel communication.
85
+ ///
86
+ /// Returns a map containing all configuration values:
87
+ /// - `sampleRate`: int
88
+ /// - `channels`: int
89
+ ///
90
+ /// Example:
91
+ /// ```dart
92
+ /// final config = SystemAudioConfig(
93
+ /// sampleRate: 44100,
94
+ /// channels: 2,
95
+ /// );
96
+ /// final map = config.toMap();
97
+ /// // map = {'sampleRate': 44100, 'channels': 2}
98
+ /// ```
99
+ Map<String, dynamic> toMap() {
100
+ return {
101
+ 'sampleRate': sampleRate,
102
+ 'channels': channels,
103
+ };
104
+ }
105
+
106
+ @override
107
+ String toString() {
108
+ return 'SystemAudioConfig(sampleRate: $sampleRate, channels: $channels)';
109
+ }
110
+ }
@@ -0,0 +1,461 @@
1
+ import 'dart:async';
2
+
3
+ import 'package:desktop_audio_capture/audio_capture.dart';
4
+ import 'package:flutter/services.dart';
5
+ export 'package:desktop_audio_capture/config/mic_audio_config.dart';
6
+
7
+ enum _MicAudioMethod {
8
+ startCapture,
9
+ stopCapture,
10
+ requestPermissions,
11
+ hasInputDevice,
12
+ getAvailableInputDevices,
13
+ }
14
+
15
+ /// Class for capturing audio from microphone input devices.
16
+ ///
17
+ /// This class allows you to capture audio from connected microphones,
18
+ /// including built-in microphones, external USB microphones, and Bluetooth
19
+ /// microphones. It requires microphone permissions.
20
+ ///
21
+ /// Example:
22
+ /// ```dart
23
+ /// final micCapture = MicAudioCapture(
24
+ /// config: MicAudioConfig(
25
+ /// sampleRate: 44100,
26
+ /// channels: 1,
27
+ /// gainBoost: 2.5,
28
+ /// ),
29
+ /// );
30
+ ///
31
+ /// // Check for available devices
32
+ /// final devices = await micCapture.getAvailableInputDevices();
33
+ /// print('Available microphones: ${devices.length}');
34
+ ///
35
+ /// await micCapture.startCapture();
36
+ ///
37
+ /// // Listen to audio stream
38
+ /// micCapture.audioStream?.listen((audioData) {
39
+ /// // Process audio bytes
40
+ /// print('Received ${audioData.length} bytes');
41
+ /// });
42
+ ///
43
+ /// // Listen to status updates
44
+ /// micCapture.statusStream?.listen((status) {
45
+ /// print('Mic active: ${status.isActive}, Device: ${status.deviceName}');
46
+ /// });
47
+ ///
48
+ /// // Listen to decibel readings
49
+ /// micCapture.decibelStream?.listen((data) {
50
+ /// print('Mic level: ${data.decibel} dB');
51
+ /// });
52
+ ///
53
+ /// // Stop when done
54
+ /// await micCapture.stopCapture();
55
+ /// ```
56
+ class MicAudioCapture extends AudioCapture {
57
+ static const MethodChannel _channel = MethodChannel(
58
+ 'com.mic_audio_transcriber/mic_capture',
59
+ );
60
+ static const EventChannel _audioStreamChannel = EventChannel(
61
+ 'com.mic_audio_transcriber/mic_stream',
62
+ );
63
+ static const EventChannel _statusStreamChannel = EventChannel(
64
+ 'com.mic_audio_transcriber/mic_status',
65
+ );
66
+ static const EventChannel _decibelStreamChannel = EventChannel(
67
+ 'com.mic_audio_transcriber/mic_decibel',
68
+ );
69
+
70
+ Stream<Uint8List>? _audioStream;
71
+ Stream<MicAudioStatus>? _statusStream;
72
+ Stream<DecibelData>? _decibelStream;
73
+ bool _isRecording = false;
74
+
75
+ /// Stream of raw audio data bytes from microphone capture.
76
+ ///
77
+ /// Returns a [Stream<Uint8List>] containing the captured audio data.
78
+ /// The stream is only available after [startCapture] has been called.
79
+ ///
80
+ /// Example:
81
+ /// ```dart
82
+ /// await micCapture.startCapture();
83
+ ///
84
+ /// micCapture.audioStream?.listen((audioData) {
85
+ /// // Process audio bytes
86
+ /// final audioBuffer = audioData.buffer.asUint8List();
87
+ /// // Use audio buffer for processing, saving, streaming, etc.
88
+ /// });
89
+ /// ```
90
+ Stream<Uint8List>? get audioStream {
91
+ // Return existing stream if available
92
+ if (_audioStream != null) {
93
+ return _audioStream;
94
+ }
95
+ // If not recording, return null
96
+ if (!_isRecording) {
97
+ return null;
98
+ }
99
+ // Create stream lazily if recording but stream not created yet
100
+ _audioStream = _audioStreamChannel.receiveBroadcastStream().map((
101
+ dynamic event,
102
+ ) {
103
+ if (event is Uint8List) {
104
+ return event;
105
+ } else if (event is List<int>) {
106
+ return Uint8List.fromList(event);
107
+ }
108
+ throw Exception('Unexpected audio data type: ${event.runtimeType}');
109
+ });
110
+ return _audioStream;
111
+ }
112
+
113
+ /// Stream of microphone status updates.
114
+ ///
115
+ /// Returns a [Stream<MicStatus>] containing:
116
+ /// - `isActive`: bool - whether mic is currently active
117
+ /// - `deviceName`: String? - name of the microphone device (if available)
118
+ ///
119
+ /// Example:
120
+ /// ```dart
121
+ /// micCapture.statusStream?.listen((status) {
122
+ /// if (status.isActive) {
123
+ /// print('Microphone is active');
124
+ /// if (status.deviceName != null) {
125
+ /// print('Using device: ${status.deviceName}');
126
+ /// }
127
+ /// } else {
128
+ /// print('Microphone is inactive');
129
+ /// }
130
+ /// });
131
+ /// ```
132
+ Stream<MicAudioStatus>? get statusStream {
133
+ // Create status stream if not already created
134
+ _statusStream ??= _statusStreamChannel.receiveBroadcastStream().map((
135
+ dynamic event,
136
+ ) {
137
+ if (event is Map) {
138
+ return MicAudioStatus.fromJson(Map<String, dynamic>.from(event));
139
+ }
140
+ return const MicAudioStatus(isActive: false);
141
+ });
142
+ return _statusStream;
143
+ }
144
+
145
+ /// Stream of microphone decibel (dB) readings.
146
+ ///
147
+ /// Returns a [Stream<DecibelData>] containing:
148
+ /// - `decibel`: double - decibel value (-120 to 0 dB)
149
+ /// - `timestamp`: double - Unix timestamp
150
+ ///
151
+ /// The stream is only available while recording is active.
152
+ ///
153
+ /// Example:
154
+ /// ```dart
155
+ /// await micCapture.startCapture();
156
+ ///
157
+ /// micCapture.decibelStream?.listen((data) {
158
+ /// print('Microphone level: ${data.decibel.toStringAsFixed(1)} dB');
159
+ /// print('Timestamp: ${DateTime.fromMillisecondsSinceEpoch((data.timestamp * 1000).toInt())}');
160
+ ///
161
+ /// // Use for visual feedback, volume meters, etc.
162
+ /// if (data.decibel > -40) {
163
+ /// print('Loud input detected!');
164
+ /// }
165
+ /// });
166
+ /// ```
167
+ Stream<DecibelData>? get decibelStream => _decibelStream;
168
+
169
+ MicAudioConfig _config = MicAudioConfig();
170
+
171
+ /// Creates a new [MicAudioCapture] instance.
172
+ ///
173
+ /// [config] is optional. If not provided, default configuration will be used
174
+ /// (sampleRate: 16000, channels: 1, bitDepth: 16, gainBoost: 2.5, inputVolume: 1.0).
175
+ ///
176
+ /// Example:
177
+ /// ```dart
178
+ /// final capture = MicAudioCapture(
179
+ /// config: MicAudioConfig(
180
+ /// sampleRate: 44100,
181
+ /// channels: 1,
182
+ /// gainBoost: 3.0,
183
+ /// inputVolume: 0.8,
184
+ /// ),
185
+ /// );
186
+ /// ```
187
+ MicAudioCapture({MicAudioConfig? config}) {
188
+ _config = config ?? MicAudioConfig();
189
+ }
190
+
191
+ /// Updates the audio capture configuration.
192
+ ///
193
+ /// This method allows you to change the configuration after the instance
194
+ /// has been created. The new configuration will be applied on the next
195
+ /// [startCapture] call.
196
+ ///
197
+ /// Example:
198
+ /// ```dart
199
+ /// final capture = MicAudioCapture();
200
+ ///
201
+ /// // Update config before starting
202
+ /// capture.updateConfig(MicAudioConfig(
203
+ /// sampleRate: 48000,
204
+ /// channels: 2,
205
+ /// gainBoost: 2.0,
206
+ /// ));
207
+ ///
208
+ /// await capture.startCapture();
209
+ /// ```
210
+ void updateConfig(MicAudioConfig config) {
211
+ _config = config;
212
+ }
213
+
214
+ /// Starts capturing audio from the microphone.
215
+ ///
216
+ /// This method will request necessary permissions (microphone permission)
217
+ /// and begin capturing audio from the default or configured microphone.
218
+ ///
219
+ /// [config] is optional. If provided, it will update the current configuration
220
+ /// before starting capture.
221
+ ///
222
+ /// Throws an [Exception] if:
223
+ /// - Permissions are not granted
224
+ /// - No microphone is available
225
+ /// - Capture fails to start
226
+ ///
227
+ /// Example:
228
+ /// ```dart
229
+ /// final capture = MicAudioCapture();
230
+ ///
231
+ /// try {
232
+ /// await capture.startCapture(
233
+ /// config: MicAudioConfig(
234
+ /// sampleRate: 44100,
235
+ /// channels: 1,
236
+ /// gainBoost: 2.5,
237
+ /// ),
238
+ /// );
239
+ /// print('Microphone capture started');
240
+ /// } catch (e) {
241
+ /// print('Failed to start: $e');
242
+ /// }
243
+ /// ```
244
+ Future<void> startCapture({MicAudioConfig? config}) async {
245
+ if (_isRecording) {
246
+ return;
247
+ }
248
+
249
+ if (config != null) {
250
+ updateConfig(config);
251
+ }
252
+
253
+ try {
254
+ await requestPermissions();
255
+
256
+ try {
257
+ final result = await _channel.invokeMethod<dynamic>(
258
+ _MicAudioMethod.startCapture.name,
259
+ _config.toMap(),
260
+ );
261
+
262
+ if (result is! bool || result != true) {
263
+ final errorMsg = result is String
264
+ ? result
265
+ : 'Failed to start microphone capture. Returned: $result';
266
+ throw Exception(errorMsg);
267
+ }
268
+ } on PlatformException catch (e) {
269
+ throw Exception(
270
+ 'Failed to start microphone capture: ${e.message ?? e.code}');
271
+ }
272
+
273
+ // Create audio stream
274
+ // Note: Stream will be subscribed by listeners, which triggers onListen on native side
275
+ _audioStream = _audioStreamChannel.receiveBroadcastStream().map((
276
+ dynamic event,
277
+ ) {
278
+ if (event is Uint8List) {
279
+ return event;
280
+ } else if (event is List<int>) {
281
+ return Uint8List.fromList(event);
282
+ }
283
+ throw Exception('Unexpected audio data type: ${event.runtimeType}');
284
+ });
285
+
286
+ // Create decibel stream
287
+ _decibelStream = _decibelStreamChannel.receiveBroadcastStream().map((
288
+ dynamic event,
289
+ ) {
290
+ if (event is Map) {
291
+ return DecibelData.fromMap(Map<String, dynamic>.from(event));
292
+ }
293
+ return DecibelData(
294
+ decibel: -120.0,
295
+ timestamp: DateTime.now().millisecondsSinceEpoch / 1000.0);
296
+ });
297
+
298
+ // Status stream is created lazily via getter, no need to recreate here
299
+
300
+ _isRecording = true;
301
+ } catch (e) {
302
+ rethrow;
303
+ }
304
+ }
305
+
306
+ /// Stops capturing microphone audio.
307
+ ///
308
+ /// This method will stop the active capture and close all associated streams.
309
+ /// If capture is not active, this method does nothing.
310
+ ///
311
+ /// Throws an [Exception] if stopping fails.
312
+ ///
313
+ /// Example:
314
+ /// ```dart
315
+ /// await micCapture.startCapture();
316
+ ///
317
+ /// // ... use audio stream ...
318
+ ///
319
+ /// await micCapture.stopCapture();
320
+ /// print('Microphone capture stopped');
321
+ /// ```
322
+ Future<void> stopCapture() async {
323
+ if (!_isRecording) return;
324
+
325
+ try {
326
+ final stoped = await _channel.invokeMethod<bool>(
327
+ _MicAudioMethod.stopCapture.name,
328
+ );
329
+
330
+ if (stoped != true) {
331
+ throw Exception("Failed to stop microphone capture");
332
+ }
333
+
334
+ _isRecording = false;
335
+ _audioStream = null;
336
+ _statusStream = null;
337
+ _decibelStream = null;
338
+ } catch (e) {
339
+ rethrow;
340
+ }
341
+ }
342
+
343
+ /// Whether microphone capture is currently recording.
344
+ ///
345
+ /// Returns `true` if capture is active, `false` otherwise.
346
+ ///
347
+ /// Example:
348
+ /// ```dart
349
+ /// if (micCapture.isRecording) {
350
+ /// print('Microphone is being captured');
351
+ /// } else {
352
+ /// print('Microphone capture is not active');
353
+ /// }
354
+ /// ```
355
+ @override
356
+ bool get isRecording => _isRecording;
357
+
358
+ /// Requests necessary permissions for microphone capture.
359
+ ///
360
+ /// This requests microphone permission which is required to capture audio
361
+ /// from input devices.
362
+ ///
363
+ /// Returns `true` if permissions are granted.
364
+ ///
365
+ /// Throws an [Exception] if permissions are not granted.
366
+ ///
367
+ /// Example:
368
+ /// ```dart
369
+ /// try {
370
+ /// final hasPermission = await micCapture.requestPermissions();
371
+ /// if (hasPermission) {
372
+ /// await micCapture.startCapture();
373
+ /// }
374
+ /// } catch (e) {
375
+ /// print('Permission denied: $e');
376
+ /// }
377
+ /// ```
378
+ Future<bool> requestPermissions() async {
379
+ final hasPermission = await _channel.invokeMethod<bool>(
380
+ _MicAudioMethod.requestPermissions.name,
381
+ );
382
+ if (hasPermission != true) {
383
+ throw Exception('Microphone permission not granted');
384
+ }
385
+ return true;
386
+ }
387
+
388
+ /// Checks if there is any available input device (microphone).
389
+ ///
390
+ /// Returns `true` if there is at least one available input device,
391
+ /// `false` if there is no available input device.
392
+ ///
393
+ /// Example:
394
+ /// ```dart
395
+ /// final hasDevice = await micCapture.hasInputDevice();
396
+ /// if (hasDevice) {
397
+ /// print('Microphone available');
398
+ /// await micCapture.startCapture();
399
+ /// } else {
400
+ /// print('No microphone found');
401
+ /// }
402
+ /// ```
403
+ Future<bool> hasInputDevice() async {
404
+ try {
405
+ final hasDevice = await _channel.invokeMethod<bool>(
406
+ _MicAudioMethod.hasInputDevice.name,
407
+ );
408
+ return hasDevice ?? false;
409
+ } catch (e) {
410
+ rethrow;
411
+ }
412
+ }
413
+
414
+ /// Gets a list of all available input devices (microphones).
415
+ ///
416
+ /// Returns a list of [InputDevice] containing device information:
417
+ /// - `id`: String - unique device ID
418
+ /// - `name`: String - device name
419
+ /// - `type`: [InputDeviceType] - device type (builtIn, bluetooth, external)
420
+ /// - `channelCount`: int - number of audio channels
421
+ /// - `isDefault`: bool - whether the device is the default device
422
+ ///
423
+ /// Example:
424
+ /// ```dart
425
+ /// final devices = await micCapture.getAvailableInputDevices();
426
+ ///
427
+ /// for (final device in devices) {
428
+ /// print('Device: ${device.name}');
429
+ /// print(' Type: ${device.type}');
430
+ /// print(' Channels: ${device.channelCount}');
431
+ /// print(' Default: ${device.isDefault}');
432
+ /// }
433
+ ///
434
+ /// // Find default device
435
+ /// final defaultDevice = devices.firstWhere(
436
+ /// (device) => device.isDefault,
437
+ /// orElse: () => devices.first,
438
+ /// );
439
+ /// print('Using device: ${defaultDevice.name}');
440
+ /// ```
441
+ Future<List<InputDevice>> getAvailableInputDevices() async {
442
+ try {
443
+ final devices = await _channel.invokeMethod<List<dynamic>>(
444
+ _MicAudioMethod.getAvailableInputDevices.name,
445
+ );
446
+
447
+ if (devices == null) {
448
+ return [];
449
+ }
450
+
451
+ return devices
452
+ .whereType<Map>()
453
+ .map((device) => InputDevice.fromMap(
454
+ Map<String, dynamic>.from(device),
455
+ ))
456
+ .toList();
457
+ } catch (e) {
458
+ rethrow;
459
+ }
460
+ }
461
+ }
@@ -0,0 +1,91 @@
1
+ abstract class AudioStatus {
2
+ final bool isActive;
3
+
4
+ const AudioStatus({
5
+ required this.isActive,
6
+ });
7
+
8
+ Map<String, dynamic> toJson();
9
+
10
+ @override
11
+ bool operator ==(Object other) {
12
+ if (identical(this, other)) return true;
13
+
14
+ return other is AudioStatus && other.isActive == isActive;
15
+ }
16
+
17
+ @override
18
+ int get hashCode => isActive.hashCode;
19
+ }
20
+
21
+ class MicAudioStatus extends AudioStatus {
22
+ final String? deviceName;
23
+
24
+ const MicAudioStatus({
25
+ required super.isActive,
26
+ this.deviceName,
27
+ });
28
+
29
+ MicAudioStatus copyWith({
30
+ bool? isActive,
31
+ String? deviceName,
32
+ }) {
33
+ return MicAudioStatus(
34
+ isActive: isActive ?? this.isActive,
35
+ deviceName: deviceName ?? this.deviceName,
36
+ );
37
+ }
38
+
39
+ factory MicAudioStatus.fromJson(Map<String, dynamic> json) {
40
+ return MicAudioStatus(
41
+ isActive: json['isActive'],
42
+ deviceName: json['deviceName'],
43
+ );
44
+ }
45
+
46
+ @override
47
+ Map<String, dynamic> toJson() {
48
+ return {
49
+ 'isActive': isActive,
50
+ 'deviceName': deviceName,
51
+ };
52
+ }
53
+
54
+ @override
55
+ String toString() =>
56
+ '''MicAudioStatus(isActive: $isActive, deviceName: $deviceName)''';
57
+
58
+ @override
59
+ bool operator ==(Object other) {
60
+ if (identical(this, other)) return true;
61
+
62
+ return other is MicAudioStatus && other.deviceName == deviceName;
63
+ }
64
+
65
+ @override
66
+ int get hashCode => deviceName.hashCode;
67
+ }
68
+
69
+ class SystemAudioStatus extends AudioStatus {
70
+ SystemAudioStatus({required super.isActive});
71
+
72
+ SystemAudioStatus copyWith({
73
+ bool? isActive,
74
+ }) {
75
+ return SystemAudioStatus(isActive: isActive ?? this.isActive);
76
+ }
77
+
78
+ factory SystemAudioStatus.fromJson(Map<String, dynamic> json) {
79
+ return SystemAudioStatus(isActive: json['isActive']);
80
+ }
81
+
82
+ @override
83
+ Map<String, dynamic> toJson() {
84
+ return {
85
+ 'isActive': isActive,
86
+ };
87
+ }
88
+
89
+ @override
90
+ String toString() => '''SystemAudioStatus(isActive: $isActive)''';
91
+ }