From 7329aafc2b16ade691cb157aea46d843f6d4da82 Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Mon, 7 Apr 2025 21:30:33 +0530 Subject: [PATCH 01/16] latest flutter web loading strategy & change flutter_sound_web version to static --- app/pubspec.yaml | 2 +- app/web/index.html | 56 +++++++++++++--------------------------------- 2 files changed, 17 insertions(+), 41 deletions(-) diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 3830da6c330..38a269e0bda 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -86,7 +86,7 @@ dependencies: geolocator: version: ^3.0.2 webview_flutter: ^4.8.0 - flutter_sound: ^9.10.0 + flutter_sound: 9.10.0 # fixed web build error just_audio: ^0.9.39 frame_sdk: ^0.0.7 web_socket_channel: ^3.0.1 diff --git a/app/web/index.html b/app/web/index.html index aab0145cdec..c449a49ef93 100644 --- a/app/web/index.html +++ b/app/web/index.html @@ -1,4 +1,6 @@ - + + + - + @@ -18,7 +23,7 @@ - + @@ -29,7 +34,7 @@ - + @@ -37,23 +42,14 @@ Omi AI - - - - - - - + @@ -138,30 +134,10 @@ } - - - - - - - + - \ No newline at end of file + + + + \ No newline at end of file From b0dcd9e7c5e349c49ea41ea5ecddf6030df95d3a Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Mon, 7 Apr 2025 22:10:40 +0530 Subject: [PATCH 02/16] refactor: intercom abstracted and disabled on web --- app/lib/main.dart | 2 +- .../widgets/app_analytics_widget.dart | 2 +- app/lib/pages/home/device.dart | 3 +- app/lib/pages/home/firmware_update.dart | 2 +- .../pages/onboarding/name/name_widget.dart | 6 ++- .../permissions/permissions_widget.dart | 5 ++- .../primary_language_widget.dart | 5 ++- app/lib/pages/onboarding/welcome/page.dart | 2 +- app/lib/pages/onboarding/wrapper.dart | 2 +- app/lib/pages/settings/about.dart | 2 +- app/lib/pages/settings/device_settings.dart | 2 +- app/lib/pages/settings/page.dart | 9 +++-- app/lib/pages/speech_profile/page.dart | 5 ++- app/lib/services/notifications.dart | 5 ++- .../utils/analytics/analytics_manager.dart | 2 +- .../{intercom.dart => intercom_manager.dart} | 38 ++++++++++++++++++- app/lib/utils/logger.dart | 5 ++- app/lib/utils/platform.dart | 13 +++++++ 18 files changed, 84 insertions(+), 26 deletions(-) rename app/lib/utils/analytics/{intercom.dart => intercom_manager.dart} (70%) create mode 100644 app/lib/utils/platform.dart diff --git a/app/lib/main.dart b/app/lib/main.dart index 81c9c86ea2a..5b2c18ebc7d 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -43,7 +43,7 @@ import 'package:omi/services/notifications.dart'; import 'package:omi/services/services.dart'; import 'package:omi/utils/alerts/app_snackbar.dart'; import 'package:omi/utils/analytics/growthbook.dart'; -import 'package:omi/utils/analytics/intercom.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; import 'package:omi/utils/analytics/mixpanel.dart'; import 'package:omi/utils/features/calendar.dart'; import 'package:omi/utils/logger.dart'; diff --git a/app/lib/pages/apps/app_detail/widgets/app_analytics_widget.dart b/app/lib/pages/apps/app_detail/widgets/app_analytics_widget.dart index 17832300e12..264d7e8071f 100644 --- a/app/lib/pages/apps/app_detail/widgets/app_analytics_widget.dart +++ b/app/lib/pages/apps/app_detail/widgets/app_analytics_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:omi/gen/assets.gen.dart'; -import 'package:omi/utils/analytics/intercom.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; import 'package:skeletonizer/skeletonizer.dart'; class AppAnalyticsWidget extends StatelessWidget { diff --git a/app/lib/pages/home/device.dart b/app/lib/pages/home/device.dart index 44e2faf73c4..d0ecb8d7639 100644 --- a/app/lib/pages/home/device.dart +++ b/app/lib/pages/home/device.dart @@ -1,9 +1,10 @@ + import 'package:flutter/material.dart'; import 'package:omi/backend/preferences.dart'; import 'package:omi/backend/schema/bt_device/bt_device.dart'; import 'package:omi/providers/device_provider.dart'; import 'package:omi/services/services.dart'; -import 'package:omi/utils/analytics/intercom.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; import 'package:omi/utils/analytics/mixpanel.dart'; import 'package:omi/widgets/device_widget.dart'; import 'package:gradient_borders/box_borders/gradient_box_border.dart'; diff --git a/app/lib/pages/home/firmware_update.dart b/app/lib/pages/home/firmware_update.dart index 9664f7e3230..99ad8b98b27 100644 --- a/app/lib/pages/home/firmware_update.dart +++ b/app/lib/pages/home/firmware_update.dart @@ -3,7 +3,7 @@ import 'firmware_update_dialog.dart'; import 'package:omi/backend/schema/bt_device/bt_device.dart'; import 'package:omi/pages/home/firmware_mixin.dart'; import 'package:omi/pages/home/page.dart'; -import 'package:omi/utils/analytics/intercom.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; import 'package:omi/utils/other/temp.dart'; import 'package:gradient_borders/gradient_borders.dart'; diff --git a/app/lib/pages/onboarding/name/name_widget.dart b/app/lib/pages/onboarding/name/name_widget.dart index 9bbe705cf0c..7b2a9d407c9 100644 --- a/app/lib/pages/onboarding/name/name_widget.dart +++ b/app/lib/pages/onboarding/name/name_widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:omi/backend/auth.dart'; import 'package:omi/backend/preferences.dart'; import 'package:gradient_borders/gradient_borders.dart'; -import 'package:intercom_flutter/intercom_flutter.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; + import 'package:omi/providers/home_provider.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; import 'package:provider/provider.dart'; class NameWidget extends StatefulWidget { @@ -117,7 +119,7 @@ class _NameWidgetState extends State { ), ), onTap: () { - Intercom.instance.displayMessenger(); + IntercomManager().displayMessenger(); }, ), ], diff --git a/app/lib/pages/onboarding/permissions/permissions_widget.dart b/app/lib/pages/onboarding/permissions/permissions_widget.dart index 03ef0d4d780..4370c60a04e 100644 --- a/app/lib/pages/onboarding/permissions/permissions_widget.dart +++ b/app/lib/pages/onboarding/permissions/permissions_widget.dart @@ -4,7 +4,8 @@ import 'package:flutter/material.dart'; import 'package:omi/providers/onboarding_provider.dart'; import 'package:omi/widgets/dialog.dart'; import 'package:gradient_borders/box_borders/gradient_box_border.dart'; -import 'package:intercom_flutter/intercom_flutter.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; + import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; @@ -229,7 +230,7 @@ class _PermissionsWidgetState extends State { ), ), onTap: () { - Intercom.instance.displayMessenger(); + IntercomManager().displayMessenger(); }, ), ], diff --git a/app/lib/pages/onboarding/primary_language/primary_language_widget.dart b/app/lib/pages/onboarding/primary_language/primary_language_widget.dart index 5935aea8f47..b879f76faa4 100644 --- a/app/lib/pages/onboarding/primary_language/primary_language_widget.dart +++ b/app/lib/pages/onboarding/primary_language/primary_language_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:gradient_borders/gradient_borders.dart'; -import 'package:intercom_flutter/intercom_flutter.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; + import 'package:omi/backend/preferences.dart'; import 'package:omi/providers/home_provider.dart'; import 'package:provider/provider.dart'; @@ -355,7 +356,7 @@ class _PrimaryLanguageWidgetState extends State { ), ), onTap: () { - Intercom.instance.displayMessenger(); + IntercomManager().displayMessenger(); }, ), ], diff --git a/app/lib/pages/onboarding/welcome/page.dart b/app/lib/pages/onboarding/welcome/page.dart index 5747f97c9aa..b8ecc337d21 100644 --- a/app/lib/pages/onboarding/welcome/page.dart +++ b/app/lib/pages/onboarding/welcome/page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:omi/providers/onboarding_provider.dart'; -import 'package:omi/utils/analytics/intercom.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; import 'package:omi/widgets/dialog.dart'; import 'package:gradient_borders/box_borders/gradient_box_border.dart'; import 'package:permission_handler/permission_handler.dart'; diff --git a/app/lib/pages/onboarding/wrapper.dart b/app/lib/pages/onboarding/wrapper.dart index f385bd90e37..70485fc5f62 100644 --- a/app/lib/pages/onboarding/wrapper.dart +++ b/app/lib/pages/onboarding/wrapper.dart @@ -16,7 +16,7 @@ import 'package:omi/pages/onboarding/welcome/page.dart'; import 'package:omi/providers/home_provider.dart'; import 'package:omi/providers/onboarding_provider.dart'; import 'package:omi/services/services.dart'; -import 'package:omi/utils/analytics/intercom.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; import 'package:omi/utils/analytics/mixpanel.dart'; import 'package:omi/utils/other/temp.dart'; import 'package:omi/widgets/device_widget.dart'; diff --git a/app/lib/pages/settings/about.dart b/app/lib/pages/settings/about.dart index a1bc7ba4b28..0981e1a9977 100644 --- a/app/lib/pages/settings/about.dart +++ b/app/lib/pages/settings/about.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:omi/pages/settings/webview.dart'; -import 'package:omi/utils/analytics/intercom.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; import 'package:omi/utils/analytics/mixpanel.dart'; import 'package:omi/utils/other/temp.dart'; import 'package:url_launcher/url_launcher.dart'; diff --git a/app/lib/pages/settings/device_settings.dart b/app/lib/pages/settings/device_settings.dart index 19ea2ee5572..32f877f7298 100644 --- a/app/lib/pages/settings/device_settings.dart +++ b/app/lib/pages/settings/device_settings.dart @@ -8,7 +8,7 @@ import 'package:omi/pages/conversations/sync_page.dart'; import 'package:omi/providers/device_provider.dart'; import 'package:omi/providers/onboarding_provider.dart'; import 'package:omi/services/services.dart'; -import 'package:omi/utils/analytics/intercom.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; import 'package:omi/utils/analytics/mixpanel.dart'; import 'package:omi/utils/other/temp.dart'; import 'package:omi/widgets/dialog.dart'; diff --git a/app/lib/pages/settings/page.dart b/app/lib/pages/settings/page.dart index 8d50db0b57f..80c69dab6f2 100644 --- a/app/lib/pages/settings/page.dart +++ b/app/lib/pages/settings/page.dart @@ -9,7 +9,8 @@ import 'package:omi/pages/settings/profile.dart'; import 'package:omi/pages/settings/widgets.dart'; import 'package:omi/utils/other/temp.dart'; import 'package:omi/widgets/dialog.dart'; -import 'package:intercom_flutter/intercom_flutter.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; + import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; @@ -62,7 +63,7 @@ class _SettingsPageState extends State { getItemAddOn2( 'Need Help? Chat with us', () async { - await Intercom.instance.displayMessenger(); + await IntercomManager().displayMessenger(); }, icon: Icons.chat, ), @@ -88,7 +89,7 @@ class _SettingsPageState extends State { getItemAddOn2( 'Guides & Tutorials', () async { - await Intercom.instance.displayHelpCenter(); + await IntercomManager().displayHelpCenter(); }, icon: Icons.help_outline_outlined, ), @@ -143,7 +144,7 @@ class _SettingsPageState extends State { getItemAddOn2( 'Need Help? Chat with us', () async { - await Intercom.instance.displayMessenger(); + await IntercomManager().displayMessenger(); }, icon: Icons.chat, ), diff --git a/app/lib/pages/speech_profile/page.dart b/app/lib/pages/speech_profile/page.dart index b881383a533..0e8eb4b3aca 100644 --- a/app/lib/pages/speech_profile/page.dart +++ b/app/lib/pages/speech_profile/page.dart @@ -12,12 +12,13 @@ import 'package:omi/providers/capture_provider.dart'; import 'package:omi/providers/home_provider.dart'; import 'package:omi/providers/speech_profile_provider.dart'; import 'package:omi/services/services.dart'; -import 'package:omi/utils/analytics/intercom.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; import 'package:omi/utils/other/temp.dart'; import 'package:omi/widgets/device_widget.dart'; import 'package:omi/widgets/dialog.dart'; import 'package:gradient_borders/box_borders/gradient_box_border.dart'; -import 'package:intercom_flutter/intercom_flutter.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; + import 'package:provider/provider.dart'; import 'percentage_bar_progress.dart'; diff --git a/app/lib/services/notifications.dart b/app/lib/services/notifications.dart index 7dbd2f40653..5dfe3f24b21 100644 --- a/app/lib/services/notifications.dart +++ b/app/lib/services/notifications.dart @@ -14,7 +14,8 @@ import 'package:omi/backend/http/api/notifications.dart'; import 'package:omi/backend/schema/message.dart'; import 'package:omi/main.dart'; import 'package:omi/pages/home/page.dart'; -import 'package:intercom_flutter/intercom_flutter.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; + class NotificationService { NotificationService._(); @@ -126,7 +127,7 @@ class NotificationService { if (token == null) return; String timeZone = await getTimeZone(); if (FirebaseAuth.instance.currentUser != null && token.isNotEmpty) { - await Intercom.instance.sendTokenToIntercom(token); + await IntercomManager().sendTokenToIntercom(token); await saveFcmTokenServer(token: token, timeZone: timeZone); } } diff --git a/app/lib/utils/analytics/analytics_manager.dart b/app/lib/utils/analytics/analytics_manager.dart index 098d6f47fc2..d9d35d29c6f 100644 --- a/app/lib/utils/analytics/analytics_manager.dart +++ b/app/lib/utils/analytics/analytics_manager.dart @@ -1,4 +1,4 @@ -import 'package:omi/utils/analytics/intercom.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; import 'package:omi/utils/analytics/mixpanel.dart'; class AnalyticsManager { diff --git a/app/lib/utils/analytics/intercom.dart b/app/lib/utils/analytics/intercom_manager.dart similarity index 70% rename from app/lib/utils/analytics/intercom.dart rename to app/lib/utils/analytics/intercom_manager.dart index a4291515a1b..47551b9127f 100644 --- a/app/lib/utils/analytics/intercom.dart +++ b/app/lib/utils/analytics/intercom_manager.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:omi/backend/preferences.dart'; import 'package:omi/env/env.dart'; import 'package:intercom_flutter/intercom_flutter.dart'; +import 'package:omi/utils/platform.dart'; class IntercomManager { static final IntercomManager _instance = IntercomManager._internal(); @@ -18,7 +19,7 @@ class IntercomManager { } Future initIntercom() async { - if (Env.intercomAppId == null) return; + if (Platform.isWeb || Env.intercomAppId == null) return; await intercom.initialize( Env.intercomAppId!, iosApiKey: Env.intercomIOSApiKey, @@ -27,6 +28,7 @@ class IntercomManager { } Future displayChargingArticle(String device) async { + if (Platform.isWeb) return; if (device == 'Omi DevKit 2') { return await intercom.displayArticle('10003257-how-to-charge-devkit2'); } else { @@ -35,22 +37,27 @@ class IntercomManager { } Future displayEarnMoneyArticle() async { + if (Platform.isWeb) return; return await intercom.displayArticle('10401566-build-publish-and-earn-with-omi-apps'); } Future displayFirmwareUpdateArticle() async { + if (Platform.isWeb) return; return await intercom.displayArticle('9995941-updating-your-devkit2-firmware'); } Future logEvent(String eventName, {Map? metaData}) async { + if (Platform.isWeb) return; return await intercom.logEvent(eventName, metaData); } Future updateCustomAttributes(Map attributes) async { + if (Platform.isWeb) return; return await intercom.updateUser(customAttributes: attributes); } Future updateUser(String? email, String? name, String? uid) async { + if (Platform.isWeb) return; return await intercom.updateUser( email: email, name: name, @@ -59,6 +66,7 @@ class IntercomManager { } Future setUserAttributes() async { + if (Platform.isWeb) return; await updateCustomAttributes({ 'Notifications Enabled': _preferences.notificationsEnabled, 'Location Enabled': _preferences.locationEnabled, @@ -70,4 +78,32 @@ class IntercomManager { 'Authorized Storing Recordings': _preferences.permissionStoreRecordingsEnabled, }); } + + + + Future displayMessenger()async{ + if (Platform.isWeb) return; + await intercom.displayMessenger(); + } + + Future displayHelpCenter() async { + if (Platform.isWeb) return; + await intercom.displayHelpCenter(); + } + + Future loginIdentifiedUser({required String userId}) async { + if (Platform.isWeb) return; + await intercom.loginIdentifiedUser(userId: userId); + } + + Future loginUnidentifiedUser() async { + if (Platform.isWeb) return; + await intercom.loginUnidentifiedUser(); + } + + Future sendTokenToIntercom(String token) async { + if (Platform.isWeb) return; + await intercom.sendTokenToIntercom(token); + } + } diff --git a/app/lib/utils/logger.dart b/app/lib/utils/logger.dart index 2a774a9d871..cfd0756eb57 100644 --- a/app/lib/utils/logger.dart +++ b/app/lib/utils/logger.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:intercom_flutter/intercom_flutter.dart'; +import 'package:omi/utils/analytics/intercom_manager.dart'; + import 'package:talker_flutter/talker_flutter.dart'; class Logger { @@ -63,7 +64,7 @@ class LoggerSnackbar extends StatelessWidget { icon: const Icon(Icons.share, color: Colors.white), onPressed: () async { // TODO: Have a custom form which can be prefilled with the error stack trace instead of opening the Gleap Homepage - await Intercom.instance.displayMessenger(); + await IntercomManager().displayMessenger(); }, ), ), diff --git a/app/lib/utils/platform.dart b/app/lib/utils/platform.dart new file mode 100644 index 00000000000..47fe10d3afc --- /dev/null +++ b/app/lib/utils/platform.dart @@ -0,0 +1,13 @@ +import 'package:flutter/foundation.dart'; + +class Platform { + static bool get isWeb => kIsWeb; + static bool get isNotWeb => !kIsWeb; + static bool get isIOS => !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS; + static bool get isAndroid => !kIsWeb && defaultTargetPlatform == TargetPlatform.android; + static bool get isWindows => !kIsWeb && defaultTargetPlatform == TargetPlatform.windows; + static bool get isMacOS => !kIsWeb && defaultTargetPlatform == TargetPlatform.macOS; + static bool get isLinux => !kIsWeb && defaultTargetPlatform == TargetPlatform.linux; + static bool get isMobile => isIOS || isAndroid; + static bool get isDesktop => isWindows || isMacOS || isLinux; +} From 7e8621c8d7b0024c8709bdca5c8c62280b969f05 Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Tue, 8 Apr 2025 16:50:36 +0530 Subject: [PATCH 03/16] refactor: flutter_foreground --- app/lib/main.dart | 36 ++++++++++++++----- app/lib/pages/home/page.dart | 3 +- app/lib/providers/capture_provider.dart | 4 +-- app/lib/providers/onboarding_provider.dart | 9 ++--- app/lib/utils/analytics/intercom_manager.dart | 26 +++++++------- app/lib/utils/audio/foreground.dart | 32 +++++++++++++++-- app/lib/utils/platform.dart | 2 +- 7 files changed, 78 insertions(+), 34 deletions(-) diff --git a/app/lib/main.dart b/app/lib/main.dart index 5b2c18ebc7d..a3bcbb23cee 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -45,9 +45,11 @@ import 'package:omi/utils/alerts/app_snackbar.dart'; import 'package:omi/utils/analytics/growthbook.dart'; import 'package:omi/utils/analytics/intercom_manager.dart'; import 'package:omi/utils/analytics/mixpanel.dart'; +import 'package:omi/utils/audio/foreground.dart'; import 'package:omi/utils/features/calendar.dart'; import 'package:omi/utils/logger.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; +import 'package:omi/utils/platform.dart'; import 'package:opus_dart/opus_dart.dart'; import 'package:opus_flutter/opus_flutter.dart' as opus_flutter; import 'package:posthog_flutter/posthog_flutter.dart'; @@ -55,16 +57,21 @@ import 'package:provider/provider.dart'; import 'package:talker_flutter/talker_flutter.dart'; Future _init() async { + + await Firebase.initializeApp( + name: PlatformUtil.isWeb + ? '[DEFAULT]' + : F.env == Environment.prod + ? "prod" + : "dev", + options: F.env == Environment.prod + ? prod.DefaultFirebaseOptions.currentPlatform + : dev.DefaultFirebaseOptions.currentPlatform, + ); + // Service manager ServiceManager.init(); - // Firebase - if (F.env == Environment.prod) { - await Firebase.initializeApp(options: prod.DefaultFirebaseOptions.currentPlatform, name: 'prod'); - } else { - await Firebase.initializeApp(options: dev.DefaultFirebaseOptions.currentPlatform, name: 'dev'); - } - await IntercomManager().initIntercom(); await NotificationService.instance.initialize(); await SharedPreferencesUtil.init(); @@ -98,7 +105,7 @@ void main() async { } else { Env.init(DevEnv()); } - FlutterForegroundTask.initCommunicationPort(); + ForegroundUtil.initCommunicationPort(); if (Env.posthogApiKey != null) { await initPostHog(); } @@ -217,7 +224,7 @@ class _MyAppState extends State with WidgetsBindingObserver { ChangeNotifierProvider(create: (context) => MemoriesProvider()), ], builder: (context, child) { - return WithForegroundTask( + return ForegroundTask( child: MaterialApp( navigatorObservers: [ if (Env.instabugApiKey != null) InstabugNavigatorObserver(), @@ -289,6 +296,17 @@ class _MyAppState extends State with WidgetsBindingObserver { } } +Widget ForegroundTask({required MaterialApp child}) { + if(PlatformUtil.isWeb) { + return child; + }else { + return WithForegroundTask( + child: child, + ); + } + +} + class DeciderWidget extends StatefulWidget { const DeciderWidget({super.key}); diff --git a/app/lib/pages/home/page.dart b/app/lib/pages/home/page.dart index 06b2077c5b6..fc20bc86e32 100644 --- a/app/lib/pages/home/page.dart +++ b/app/lib/pages/home/page.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:flutter_foreground_task/flutter_foreground_task.dart'; import 'package:omi/gen/assets.gen.dart'; import 'package:omi/pages/persona/persona_provider.dart'; import 'package:omi/backend/http/api/users.dart'; @@ -255,7 +254,7 @@ class _HomePageState extends State with WidgetsBindingObserver, Ticker super.initState(); // After init - FlutterForegroundTask.addTaskDataCallback(_onReceiveTaskData); + ForegroundUtil.addTaskDataCallback(_onReceiveTaskData); } void _listenToMessagesFromNotification() { diff --git a/app/lib/providers/capture_provider.dart b/app/lib/providers/capture_provider.dart index a5d7eeb2999..fa4e0b2ed8d 100644 --- a/app/lib/providers/capture_provider.dart +++ b/app/lib/providers/capture_provider.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter/material.dart'; -import 'package:flutter_foreground_task/flutter_foreground_task.dart'; import 'package:flutter_provider_utilities/flutter_provider_utilities.dart'; import 'package:omi/backend/http/api/conversations.dart'; import 'package:omi/backend/preferences.dart'; @@ -23,6 +22,7 @@ import 'package:omi/services/sockets/sdcard_socket.dart'; import 'package:omi/services/sockets/transcription_connection.dart'; import 'package:omi/services/wals.dart'; import 'package:omi/utils/analytics/mixpanel.dart'; +import 'package:omi/utils/audio/foreground.dart'; import 'package:omi/utils/enums.dart'; import 'package:omi/utils/logger.dart'; import 'package:internet_connection_checker_plus/internet_connection_checker_plus.dart'; @@ -615,7 +615,7 @@ class CaptureProvider extends ChangeNotifier if (segments.isEmpty) { debugPrint('newSegments: ${newSegments.last}'); - FlutterForegroundTask.sendDataToTask(jsonEncode({'location': true})); + ForegroundUtil.sendDataToTask(jsonEncode({'location': true})); _loadInProgressConversation(); } TranscriptSegment.combineSegments(segments, newSegments); diff --git a/app/lib/providers/onboarding_provider.dart b/app/lib/providers/onboarding_provider.dart index 3797c84e580..7d3a3ad053b 100644 --- a/app/lib/providers/onboarding_provider.dart +++ b/app/lib/providers/onboarding_provider.dart @@ -5,7 +5,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; -import 'package:flutter_foreground_task/flutter_foreground_task.dart'; import 'package:flutter_provider_utilities/flutter_provider_utilities.dart'; import 'package:omi/backend/preferences.dart'; import 'package:omi/backend/schema/bt_device/bt_device.dart'; @@ -114,10 +113,12 @@ class OnboardingProvider extends BaseProvider with MessageNotifierMixin implemen } Future askForBackgroundPermissions() async { - await FlutterForegroundTask.requestIgnoreBatteryOptimization(); + await ForegroundUtil.requestIgnoreBatteryOptimization(); // Use abstraction var isAllowed = await ForegroundUtil().isIgnoringBatteryOptimizations; - updateBackgroundPermission(isAllowed); - notifyListeners(); + if (isAllowed != null) { + updateBackgroundPermission(isAllowed); + notifyListeners(); + } } Future<(bool, PermissionStatus)> askForLocationPermissions() async { diff --git a/app/lib/utils/analytics/intercom_manager.dart b/app/lib/utils/analytics/intercom_manager.dart index 47551b9127f..0a663754e78 100644 --- a/app/lib/utils/analytics/intercom_manager.dart +++ b/app/lib/utils/analytics/intercom_manager.dart @@ -19,7 +19,7 @@ class IntercomManager { } Future initIntercom() async { - if (Platform.isWeb || Env.intercomAppId == null) return; + if (PlatformUtil.isWeb || Env.intercomAppId == null) return; await intercom.initialize( Env.intercomAppId!, iosApiKey: Env.intercomIOSApiKey, @@ -28,7 +28,7 @@ class IntercomManager { } Future displayChargingArticle(String device) async { - if (Platform.isWeb) return; + if (PlatformUtil.isWeb) return; if (device == 'Omi DevKit 2') { return await intercom.displayArticle('10003257-how-to-charge-devkit2'); } else { @@ -37,27 +37,27 @@ class IntercomManager { } Future displayEarnMoneyArticle() async { - if (Platform.isWeb) return; + if (PlatformUtil.isWeb) return; return await intercom.displayArticle('10401566-build-publish-and-earn-with-omi-apps'); } Future displayFirmwareUpdateArticle() async { - if (Platform.isWeb) return; + if (PlatformUtil.isWeb) return; return await intercom.displayArticle('9995941-updating-your-devkit2-firmware'); } Future logEvent(String eventName, {Map? metaData}) async { - if (Platform.isWeb) return; + if (PlatformUtil.isWeb) return; return await intercom.logEvent(eventName, metaData); } Future updateCustomAttributes(Map attributes) async { - if (Platform.isWeb) return; + if (PlatformUtil.isWeb) return; return await intercom.updateUser(customAttributes: attributes); } Future updateUser(String? email, String? name, String? uid) async { - if (Platform.isWeb) return; + if (PlatformUtil.isWeb) return; return await intercom.updateUser( email: email, name: name, @@ -66,7 +66,7 @@ class IntercomManager { } Future setUserAttributes() async { - if (Platform.isWeb) return; + if (PlatformUtil.isWeb) return; await updateCustomAttributes({ 'Notifications Enabled': _preferences.notificationsEnabled, 'Location Enabled': _preferences.locationEnabled, @@ -82,27 +82,27 @@ class IntercomManager { Future displayMessenger()async{ - if (Platform.isWeb) return; + if (PlatformUtil.isWeb) return; await intercom.displayMessenger(); } Future displayHelpCenter() async { - if (Platform.isWeb) return; + if (PlatformUtil.isWeb) return; await intercom.displayHelpCenter(); } Future loginIdentifiedUser({required String userId}) async { - if (Platform.isWeb) return; + if (PlatformUtil.isWeb) return; await intercom.loginIdentifiedUser(userId: userId); } Future loginUnidentifiedUser() async { - if (Platform.isWeb) return; + if (PlatformUtil.isWeb) return; await intercom.loginUnidentifiedUser(); } Future sendTokenToIntercom(String token) async { - if (Platform.isWeb) return; + if (PlatformUtil.isWeb) return; await intercom.sendTokenToIntercom(token); } diff --git a/app/lib/utils/audio/foreground.dart b/app/lib/utils/audio/foreground.dart index b8a1f7bd15f..ef5fae8d5b0 100644 --- a/app/lib/utils/audio/foreground.dart +++ b/app/lib/utils/audio/foreground.dart @@ -1,8 +1,8 @@ -import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_foreground_task/flutter_foreground_task.dart'; import 'package:geolocator/geolocator.dart'; +import 'package:omi/utils/platform.dart'; @pragma('vm:entry-point') void _startForegroundCallback() { @@ -64,7 +64,29 @@ class _ForegroundFirstTaskHandler extends TaskHandler { } class ForegroundUtil { + // Add this new method + static void sendDataToTask(Object data) { + if(PlatformUtil.isWeb) return; + FlutterForegroundTask.sendDataToTask(data); + } + + static void addTaskDataCallback(Function(Object) callback) { + if(PlatformUtil.isWeb) return; + FlutterForegroundTask.addTaskDataCallback(callback); + } + + static void initCommunicationPort() { + if(PlatformUtil.isWeb) return; + FlutterForegroundTask.initCommunicationPort(); + } + + static requestIgnoreBatteryOptimization() async { + if(PlatformUtil.isWeb) return; + await FlutterForegroundTask.requestIgnoreBatteryOptimization(); + } + static Future requestPermissions() async { + if(PlatformUtil.isWeb) return; // Android 13+, you need to allow notification permission to display foreground service notification. // // iOS: If you need notification, ask for permission. @@ -74,7 +96,7 @@ class ForegroundUtil { await FlutterForegroundTask.requestNotificationPermission(); } - if (Platform.isAndroid) { + if (PlatformUtil.isAndroid) { // if (!await FlutterForegroundTask.canDrawOverlays) { // await FlutterForegroundTask.openSystemAlertWindowSettings(); // } @@ -84,9 +106,13 @@ class ForegroundUtil { } } - Future get isIgnoringBatteryOptimizations async => await FlutterForegroundTask.isIgnoringBatteryOptimizations; + Future get isIgnoringBatteryOptimizations async { + if(PlatformUtil.isWeb) null; + return await FlutterForegroundTask.isIgnoringBatteryOptimizations; + } static Future initializeForegroundService() async { + if(PlatformUtil.isWeb) return; if (await FlutterForegroundTask.isRunningService) return; debugPrint('initializeForegroundService'); // await Location().requestPermission(); diff --git a/app/lib/utils/platform.dart b/app/lib/utils/platform.dart index 47fe10d3afc..9bdef8019a5 100644 --- a/app/lib/utils/platform.dart +++ b/app/lib/utils/platform.dart @@ -1,6 +1,6 @@ import 'package:flutter/foundation.dart'; -class Platform { +class PlatformUtil { static bool get isWeb => kIsWeb; static bool get isNotWeb => !kIsWeb; static bool get isIOS => !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS; From 4a3151a6fa5f8230ac3e993d10efe35530b5ceae Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Tue, 8 Apr 2025 17:58:32 +0530 Subject: [PATCH 04/16] refactor(platform): replace Platform checks with PlatformUtil for consistency This commit replaces all direct `Platform` checks with the custom `PlatformUtil` utility to ensure consistent platform detection across the application. This change improves maintainability and reduces redundancy in platform-specific logic. --- app/lib/main.dart | 14 ++++++++++---- app/lib/pages/chat/page.dart | 5 +++-- app/lib/pages/conversation_detail/maps_util.dart | 3 ++- app/lib/pages/home/page.dart | 3 ++- .../memories/widgets/delete_confirmation.dart | 3 ++- app/lib/pages/onboarding/auth.dart | 3 ++- .../onboarding/permissions/permissions_widget.dart | 5 +++-- app/lib/pages/settings/change_name_widget.dart | 3 ++- app/lib/pages/settings/people.dart | 7 ++++--- app/lib/providers/onboarding_provider.dart | 5 +++-- app/lib/services/devices/device_connection.dart | 3 ++- app/lib/services/devices/models.dart | 2 +- app/lib/services/devices/omi_connection.dart | 13 +++++++------ app/lib/services/notifications.dart | 13 +++++++++++-- app/lib/services/services.dart | 3 ++- app/lib/utils/alerts/app_dialog.dart | 3 ++- app/lib/utils/analytics/growthbook.dart | 3 ++- app/lib/utils/device.dart | 3 ++- app/lib/utils/other/temp.dart | 3 ++- app/lib/widgets/confirmation_dialog.dart | 3 ++- app/lib/widgets/dialog.dart | 3 ++- app/lib/widgets/sign_in_button.dart | 5 +++-- app/web/index.html | 2 +- 23 files changed, 72 insertions(+), 38 deletions(-) diff --git a/app/lib/main.dart b/app/lib/main.dart index a3bcbb23cee..0f0f89d14d9 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -86,7 +86,7 @@ Future _init() async { await GrowthbookUtil.init(); CalendarUtil.init(); - ble.FlutterBluePlus.setLogLevel(ble.LogLevel.info, color: true); + if(PlatformUtil.isNotWeb) ble.FlutterBluePlus.setLogLevel(ble.LogLevel.info, color: true); return isAuth; } @@ -329,6 +329,12 @@ class _DeciderWidgetState extends State { } void openAppLink(Uri uri) async { + // Add this check before accessing path segments + if (uri.pathSegments.isEmpty) { + debugPrint('URI has no path segments: $uri'); + return; + } + if (uri.pathSegments.first == 'apps') { if (mounted) { var app = await context.read().getAppFromId(uri.pathSegments[1]); @@ -359,7 +365,7 @@ class _DeciderWidgetState extends State { context.read().setupHasSpeakerProfile(); context.read().setupUserPrimaryLanguage(); try { - await IntercomManager.instance.intercom.loginIdentifiedUser( + await IntercomManager().loginIdentifiedUser( userId: SharedPreferencesUtil().uid, ); } catch (e) { @@ -370,9 +376,9 @@ class _DeciderWidgetState extends State { context.read().setAppsFromCache(); context.read().refreshMessages(); } else { - await IntercomManager.instance.intercom.loginUnidentifiedUser(); + await IntercomManager().loginUnidentifiedUser(); } - IntercomManager.instance.setUserAttributes(); + IntercomManager().setUserAttributes(); }); super.initState(); } diff --git a/app/lib/pages/chat/page.dart b/app/lib/pages/chat/page.dart index 3b8a1b724a3..9555268d5eb 100644 --- a/app/lib/pages/chat/page.dart +++ b/app/lib/pages/chat/page.dart @@ -20,6 +20,7 @@ import 'package:omi/providers/conversation_provider.dart'; import 'package:omi/providers/message_provider.dart'; import 'package:omi/utils/analytics/mixpanel.dart'; import 'package:omi/utils/other/temp.dart'; +import 'package:omi/utils/platform.dart'; import 'package:omi/widgets/dialog.dart'; import 'package:omi/widgets/extensions/string.dart'; import 'package:gradient_borders/gradient_borders.dart'; @@ -177,10 +178,10 @@ class ChatPageState extends State with AutomaticKeepAliveClientMixin { double bottomPadding = chatIndex == 0 ? provider.selectedFiles.isNotEmpty - ? (Platform.isAndroid + ? (PlatformUtil.isAndroid ? MediaQuery.sizeOf(context).height * 0.32 : MediaQuery.sizeOf(context).height * 0.3) - : (Platform.isAndroid + : (PlatformUtil.isAndroid ? MediaQuery.sizeOf(context).height * 0.21 : MediaQuery.sizeOf(context).height * 0.19) : 0; diff --git a/app/lib/pages/conversation_detail/maps_util.dart b/app/lib/pages/conversation_detail/maps_util.dart index c59d0771355..ef91d18b717 100644 --- a/app/lib/pages/conversation_detail/maps_util.dart +++ b/app/lib/pages/conversation_detail/maps_util.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:omi/env/env.dart'; import 'package:map_launcher/map_launcher.dart'; +import 'package:omi/utils/platform.dart'; class MapsUtil { static String getMapImageUrl(double lat, double lng) { @@ -15,7 +16,7 @@ class MapsUtil { } static void launchMap(double lat, double lng) async { - if (Platform.isIOS) { + if (PlatformUtil.isIOS) { await MapLauncher.showMarker( mapType: MapType.apple, coords: Coords(lat, lng), diff --git a/app/lib/pages/home/page.dart b/app/lib/pages/home/page.dart index fc20bc86e32..973d0980edd 100644 --- a/app/lib/pages/home/page.dart +++ b/app/lib/pages/home/page.dart @@ -30,6 +30,7 @@ import 'package:omi/utils/analytics/analytics_manager.dart'; import 'package:omi/utils/analytics/mixpanel.dart'; import 'package:omi/utils/audio/foreground.dart'; import 'package:omi/utils/other/temp.dart'; +import 'package:omi/utils/platform.dart'; import 'package:omi/widgets/upgrade_alert.dart'; import 'package:gradient_borders/gradient_borders.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; @@ -273,7 +274,7 @@ class _HomePageState extends State with WidgetsBindingObserver, Ticker Widget build(BuildContext context) { return MyUpgradeAlert( upgrader: _upgrader, - dialogStyle: Platform.isIOS ? UpgradeDialogStyle.cupertino : UpgradeDialogStyle.material, + dialogStyle: PlatformUtil.isIOS ? UpgradeDialogStyle.cupertino : UpgradeDialogStyle.material, child: Consumer( builder: (ctx, connectivityProvider, child) { bool isConnected = connectivityProvider.isConnected; diff --git a/app/lib/pages/memories/widgets/delete_confirmation.dart b/app/lib/pages/memories/widgets/delete_confirmation.dart index aad836fcc7c..f8dab314505 100644 --- a/app/lib/pages/memories/widgets/delete_confirmation.dart +++ b/app/lib/pages/memories/widgets/delete_confirmation.dart @@ -2,13 +2,14 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:omi/utils/platform.dart'; class DeleteConfirmation { static Future show(BuildContext context, {String? title, String? content}) async { title ??= 'Delete memory?'; content ??= 'This action cannot be undone.'; - if (Platform.isIOS) { + if (PlatformUtil.isIOS) { return await showCupertinoDialog( context: context, builder: (context) => CupertinoAlertDialog( diff --git a/app/lib/pages/onboarding/auth.dart b/app/lib/pages/onboarding/auth.dart index a62ef06b6a8..9f280d241d4 100644 --- a/app/lib/pages/onboarding/auth.dart +++ b/app/lib/pages/onboarding/auth.dart @@ -4,6 +4,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:omi/backend/preferences.dart'; import 'package:omi/providers/auth_provider.dart'; +import 'package:omi/utils/platform.dart'; import 'package:omi/widgets/sign_in_button.dart'; import 'package:provider/provider.dart'; @@ -38,7 +39,7 @@ class _AuthComponentState extends State { ), ), SizedBox(height: MediaQuery.of(context).textScaleFactor > 1.0 ? 18 : 32), - if (Platform.isIOS) ...[ + if (PlatformUtil.isIOS) ...[ SignInButton.withApple( title: 'Sign in with Apple', onTap: () async { diff --git a/app/lib/pages/onboarding/permissions/permissions_widget.dart b/app/lib/pages/onboarding/permissions/permissions_widget.dart index 4370c60a04e..703a44cd357 100644 --- a/app/lib/pages/onboarding/permissions/permissions_widget.dart +++ b/app/lib/pages/onboarding/permissions/permissions_widget.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:omi/providers/onboarding_provider.dart'; +import 'package:omi/utils/platform.dart'; import 'package:omi/widgets/dialog.dart'; import 'package:gradient_borders/box_borders/gradient_box_border.dart'; import 'package:omi/utils/analytics/intercom_manager.dart'; @@ -27,7 +28,7 @@ class _PermissionsWidgetState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ - Platform.isAndroid + PlatformUtil.isAndroid ? CheckboxListTile( value: provider.hasBackgroundPermission, onChanged: (s) async { @@ -163,7 +164,7 @@ class _PermissionsWidgetState extends State { padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), onPressed: () async { provider.setLoading(true); - if (Platform.isAndroid) { + if (PlatformUtil.isAndroid) { if (!provider.hasBackgroundPermission) { await provider.askForBackgroundPermissions(); } diff --git a/app/lib/pages/settings/change_name_widget.dart b/app/lib/pages/settings/change_name_widget.dart index cf47d35d321..9a0572ddde3 100644 --- a/app/lib/pages/settings/change_name_widget.dart +++ b/app/lib/pages/settings/change_name_widget.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:omi/backend/auth.dart'; import 'package:omi/backend/preferences.dart'; import 'package:omi/utils/alerts/app_snackbar.dart'; +import 'package:omi/utils/platform.dart'; class ChangeNameWidget extends StatefulWidget { const ChangeNameWidget({super.key}); @@ -28,7 +29,7 @@ class _ChangeNameWidgetState extends State { @override Widget build(BuildContext context) { - if (Platform.isIOS) { + if (PlatformUtil.isIOS) { return CupertinoAlertDialog( content: Padding( padding: const EdgeInsets.all(8.0), diff --git a/app/lib/pages/settings/people.dart b/app/lib/pages/settings/people.dart index 847bf6d1800..867aca39140 100644 --- a/app/lib/pages/settings/people.dart +++ b/app/lib/pages/settings/people.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:omi/backend/schema/person.dart'; import 'package:omi/providers/people_provider.dart'; import 'package:omi/providers/connectivity_provider.dart'; +import 'package:omi/utils/platform.dart'; import 'package:omi/widgets/dialog.dart'; import 'package:omi/widgets/extensions/functions.dart'; import 'package:just_audio/just_audio.dart'; @@ -40,7 +41,7 @@ class _UserPeoplePageState extends State<_UserPeoplePage> { } Widget _showPersonDialogForm(formKey, nameController) { - return Platform.isIOS + return PlatformUtil.isIOS ? Material( color: Colors.transparent, child: Theme( @@ -107,7 +108,7 @@ class _UserPeoplePageState extends State<_UserPeoplePage> { } } - return Platform.isIOS + return PlatformUtil.isIOS ? [ CupertinoDialogAction( onPressed: () => Navigator.pop(context), @@ -142,7 +143,7 @@ class _UserPeoplePageState extends State<_UserPeoplePage> { await showDialog( context: context, - builder: (BuildContext context) => Platform.isIOS + builder: (BuildContext context) => PlatformUtil.isIOS ? CupertinoAlertDialog( title: Text(person == null ? 'Add New Person' : 'Edit Person'), content: _showPersonDialogForm(formKey, nameController), diff --git a/app/lib/providers/onboarding_provider.dart b/app/lib/providers/onboarding_provider.dart index 7d3a3ad053b..24390b1010b 100644 --- a/app/lib/providers/onboarding_provider.dart +++ b/app/lib/providers/onboarding_provider.dart @@ -16,6 +16,7 @@ import 'package:omi/services/services.dart'; import 'package:omi/utils/alerts/app_snackbar.dart'; import 'package:omi/utils/analytics/analytics_manager.dart'; import 'package:omi/utils/audio/foreground.dart'; +import 'package:omi/utils/platform.dart'; import 'package:permission_handler/permission_handler.dart'; class OnboardingProvider extends BaseProvider with MessageNotifierMixin implements IDeviceServiceSubsciption { @@ -80,12 +81,12 @@ class OnboardingProvider extends BaseProvider with MessageNotifierMixin implemen Future askForBluetoothPermissions() async { FlutterBluePlus.setLogLevel(LogLevel.info, color: true); - if (Platform.isIOS) { + if (PlatformUtil.isIOS) { PermissionStatus bleStatus = await Permission.bluetooth.request(); debugPrint('bleStatus: $bleStatus'); updateBluetoothPermission(bleStatus.isGranted); } else { - if (Platform.isAndroid) { + if (PlatformUtil.isAndroid) { if (FlutterBluePlus.adapterStateNow != BluetoothAdapterState.on) { try { await FlutterBluePlus.turnOn(); diff --git a/app/lib/services/devices/device_connection.dart b/app/lib/services/devices/device_connection.dart index 69e064d0256..86dbf27f255 100644 --- a/app/lib/services/devices/device_connection.dart +++ b/app/lib/services/devices/device_connection.dart @@ -9,6 +9,7 @@ import 'package:omi/services/devices.dart'; import 'package:omi/services/devices/frame_connection.dart'; import 'package:omi/services/devices/omi_connection.dart'; import 'package:omi/services/notifications.dart'; +import 'package:omi/utils/platform.dart'; class DeviceConnectionFactory { static DeviceConnection? create( @@ -82,7 +83,7 @@ abstract class DeviceConnection { } // Mtu - if (Platform.isAndroid && bleDevice.mtuNow < 512) { + if (PlatformUtil.isAndroid && bleDevice.mtuNow < 512) { await bleDevice.requestMtu(512); // This might fix the code 133 error } diff --git a/app/lib/services/devices/models.dart b/app/lib/services/devices/models.dart index 8a37da2f239..46a34b35682 100644 --- a/app/lib/services/devices/models.dart +++ b/app/lib/services/devices/models.dart @@ -46,7 +46,7 @@ Future> getBleServices(String deviceId) async { return []; } else { // TODO: need to be fixed for open glass - // if (Platform.isAndroid && device.servicesList.isNotEmpty) return device.servicesList; + // if (PlatformUtil.isAndroid && device.servicesList.isNotEmpty) return device.servicesList; if (device.servicesList.isNotEmpty) return device.servicesList; return await device.discoverServices(); } diff --git a/app/lib/services/devices/omi_connection.dart b/app/lib/services/devices/omi_connection.dart index 1a2c3eaa55a..aa6c5701a4a 100644 --- a/app/lib/services/devices/omi_connection.dart +++ b/app/lib/services/devices/omi_connection.dart @@ -13,6 +13,7 @@ import 'package:omi/services/devices/errors.dart'; import 'package:omi/services/devices/models.dart'; import 'package:omi/utils/audio/wav_bytes.dart'; import 'package:omi/utils/logger.dart'; +import 'package:omi/utils/platform.dart'; class OmiDeviceConnection extends DeviceConnection { BluetoothService? _batteryService; @@ -166,7 +167,7 @@ class OmiDeviceConnection extends DeviceConnection { // https://github.com/chipweinberger/flutter_blue_plus/issues/612 final device = bleDevice; if (device.isConnected) { - if (Platform.isAndroid && device.mtuNow < 512) { + if (PlatformUtil.isAndroid && device.mtuNow < 512) { await device.requestMtu(512); // This might fix the code 133 error } if (device.isConnected) { @@ -197,7 +198,7 @@ class OmiDeviceConnection extends DeviceConnection { // This will cause a crash in OpenGlass devices // due to a race with discoverServices() that triggers // a bug in the device firmware. - if (Platform.isAndroid && device.isConnected) await device.requestMtu(512); + if (PlatformUtil.isAndroid && device.isConnected) await device.requestMtu(512); return listener; } @@ -223,7 +224,7 @@ class OmiDeviceConnection extends DeviceConnection { // https://github.com/chipweinberger/flutter_blue_plus/issues/612 final device = bleDevice; if (device.isConnected) { - if (Platform.isAndroid && device.mtuNow < 512) { + if (PlatformUtil.isAndroid && device.mtuNow < 512) { await device.requestMtu(512); // This might fix the code 133 error } if (device.isConnected) { @@ -253,7 +254,7 @@ class OmiDeviceConnection extends DeviceConnection { // This will cause a crash in OpenGlass devices // due to a race with discoverServices() that triggers // a bug in the device firmware. - if (Platform.isAndroid && device.isConnected) await device.requestMtu(512); + if (PlatformUtil.isAndroid && device.isConnected) await device.requestMtu(512); return listener; } @@ -384,7 +385,7 @@ class OmiDeviceConnection extends DeviceConnection { // This will cause a crash in OpenGlass devices // due to a race with discoverServices() that triggers // a bug in the device firmware. - if (Platform.isAndroid) await device.requestMtu(512); + if (PlatformUtil.isAndroid) await device.requestMtu(512); return listener; } @@ -518,7 +519,7 @@ class OmiDeviceConnection extends DeviceConnection { // This will cause a crash in OpenGlass devices // due to a race with discoverServices() that triggers // a bug in the device firmware. - // if (Platform.isAndroid) await device.requestMtu(512); + // if (PlatformUtil.isAndroid) await device.requestMtu(512); return listener; } diff --git a/app/lib/services/notifications.dart b/app/lib/services/notifications.dart index 5dfe3f24b21..288657b06e2 100644 --- a/app/lib/services/notifications.dart +++ b/app/lib/services/notifications.dart @@ -15,6 +15,7 @@ import 'package:omi/backend/schema/message.dart'; import 'package:omi/main.dart'; import 'package:omi/pages/home/page.dart'; import 'package:omi/utils/analytics/intercom_manager.dart'; +import 'package:omi/utils/platform.dart'; class NotificationService { @@ -38,6 +39,7 @@ class NotificationService { final AwesomeNotifications _awesomeNotifications = AwesomeNotifications(); Future initialize() async { + if (PlatformUtil.isWeb) return; await _initializeAwesomeNotifications(); // Calling it here because the APNS token can sometimes arrive early or it might take some time (like a few seconds) // Reference: https://github.com/firebase/flutterfire/issues/12244#issuecomment-1969286794 @@ -94,6 +96,7 @@ class NotificationService { } Future requestNotificationPermissions() async { + if (PlatformUtil.isWeb) return false; bool isAllowed = await _awesomeNotifications.isNotificationAllowed(); if (!isAllowed) { isAllowed = await _awesomeNotifications.requestPermissionToSendNotifications(); @@ -105,6 +108,7 @@ class NotificationService { // Whereever this method is awaited, it will cause the app to not move forwared in execution due to it being a method call. // This was also the culprit when we had the app freeze on splash screen. Future register() async { + if (PlatformUtil.isWeb) return; try { await platform.invokeMethod( 'setNotificationOnKillService', @@ -133,7 +137,8 @@ class NotificationService { } void saveNotificationToken() async { - if (Platform.isIOS) { + if (PlatformUtil.isWeb) return; + if (PlatformUtil.isIOS) { await _firebaseMessaging.getAPNSToken(); } String? token = await _firebaseMessaging.getToken(); @@ -151,6 +156,7 @@ class NotificationService { int notificationId = 1, Map? payload, }) async { + if (PlatformUtil.isWeb) return; var allowed = await _awesomeNotifications.isNotificationAllowed(); debugPrint('createNotification: $allowed'); if (!allowed) return; @@ -162,10 +168,11 @@ class NotificationService { // FIXME: Causes the different behavior on android and iOS bool _shouldShowForegroundNotificationOnFCMMessageReceived() { - return Platform.isAndroid; + return PlatformUtil.isAndroid; } Future listenForMessages() async { + if (PlatformUtil.isWeb) return; FirebaseMessaging.onMessage.listen((RemoteMessage message) { final data = message.data; final noti = message.notification; @@ -205,6 +212,7 @@ class NotificationService { {required RemoteNotification noti, NotificationLayout layout = NotificationLayout.Default, Map? payload}) async { + if (PlatformUtil.isWeb) return; final id = Random().nextInt(10000); showNotification(id: id, title: noti.title!, body: noti.body!, layout: layout, payload: payload); } @@ -219,6 +227,7 @@ class NotificationUtil { } static Future initializeIsolateReceivePort() async { + if (PlatformUtil.isWeb) return; receivePort = ReceivePort('Notification action port in main isolate'); receivePort!.listen((serializedData) { final receivedAction = ReceivedAction().fromMap(serializedData); diff --git a/app/lib/services/services.dart b/app/lib/services/services.dart index c2afbf0fa57..3a2495fc666 100644 --- a/app/lib/services/services.dart +++ b/app/lib/services/services.dart @@ -8,6 +8,7 @@ import 'package:flutter_sound/flutter_sound.dart'; import 'package:omi/services/devices.dart'; import 'package:omi/services/sockets.dart'; import 'package:omi/services/wals.dart'; +import 'package:omi/utils/platform.dart'; class ServiceManager { late IMicRecorderService _mic; @@ -81,7 +82,7 @@ Future onStart(ServiceInstance service) async { // Recorder MicRecorderService? recorder; service.on('recorder.start').listen((event) async { - recorder = MicRecorderService(isInBG: Platform.isAndroid ? true : false); + recorder = MicRecorderService(isInBG: PlatformUtil.isAndroid ? true : false); recorder?.start(onByteReceived: (bytes) { Uint8List audioBytes = bytes; List audioBytesList = audioBytes.toList(); diff --git a/app/lib/utils/alerts/app_dialog.dart b/app/lib/utils/alerts/app_dialog.dart index bf0945514ea..d078d424cba 100644 --- a/app/lib/utils/alerts/app_dialog.dart +++ b/app/lib/utils/alerts/app_dialog.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:omi/main.dart'; +import 'package:omi/utils/platform.dart'; class AppDialog { static _getDialog({ @@ -36,7 +37,7 @@ class AppDialog { ), ), ]; - if (Platform.isIOS) { + if (PlatformUtil.isIOS) { return CupertinoAlertDialog( title: Text(title), content: Text(content), diff --git a/app/lib/utils/analytics/growthbook.dart b/app/lib/utils/analytics/growthbook.dart index b1420d2bbbf..e6fd85eb269 100644 --- a/app/lib/utils/analytics/growthbook.dart +++ b/app/lib/utils/analytics/growthbook.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:omi/backend/preferences.dart'; import 'package:omi/env/env.dart'; import 'package:growthbook_sdk_flutter/growthbook_sdk_flutter.dart'; +import 'package:omi/utils/platform.dart'; class GrowthbookUtil { static final GrowthbookUtil _instance = GrowthbookUtil._internal(); @@ -20,7 +21,7 @@ class GrowthbookUtil { print('GrowthbookUtil init'); var attr = { 'id': SharedPreferencesUtil().uid, - 'device': Platform.isAndroid ? 'android' : 'ios', + 'device': PlatformUtil.isAndroid ? 'android' : 'ios', }; _gb = await GBSDKBuilderApp( apiKey: Env.growthbookApiKey!, diff --git a/app/lib/utils/device.dart b/app/lib/utils/device.dart index 2e5e6c7d67d..d6801509823 100644 --- a/app/lib/utils/device.dart +++ b/app/lib/utils/device.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'package:omi/utils/platform.dart'; import 'package:version/version.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -30,7 +31,7 @@ class DeviceUtils { if (Version.parse(packageInfo.version) <= Version.parse(latestFirmwareDetails['min_app_version']) && int.parse(packageInfo.buildNumber) < int.parse(latestFirmwareDetails['min_app_version_code'])) { return ( - 'The latest version of firmware is not compatible with this version of App (${packageInfo.version}+${packageInfo.buildNumber}). Please update the app from ${Platform.isAndroid ? 'Play Store' : 'App Store'}', + 'The latest version of firmware is not compatible with this version of App (${packageInfo.version}+${packageInfo.buildNumber}). Please update the app from ${PlatformUtil.isAndroid ? 'Play Store' : 'App Store'}', false, latestVersionStr ); diff --git a/app/lib/utils/other/temp.dart b/app/lib/utils/other/temp.dart index 1a8a2d75f26..f6bcbef3ac7 100644 --- a/app/lib/utils/other/temp.dart +++ b/app/lib/utils/other/temp.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:omi/utils/platform.dart'; String dateTimeFormat(String format, DateTime? dateTime, {String? locale}) { if (dateTime == null) return ''; @@ -10,7 +11,7 @@ String dateTimeFormat(String format, DateTime? dateTime, {String? locale}) { } Future routeToPage(BuildContext context, Widget page, {bool replace = false}) { - var route = Platform.isIOS ? CupertinoPageRoute(builder: (c) => page) : MaterialPageRoute(builder: (c) => page); + var route = PlatformUtil.isIOS ? CupertinoPageRoute(builder: (c) => page) : MaterialPageRoute(builder: (c) => page); if (replace) { if (context.mounted) { return Navigator.of(context).pushAndRemoveUntil(route, (route) => false); diff --git a/app/lib/widgets/confirmation_dialog.dart b/app/lib/widgets/confirmation_dialog.dart index d32105a8dd0..ce3bfec30c7 100644 --- a/app/lib/widgets/confirmation_dialog.dart +++ b/app/lib/widgets/confirmation_dialog.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:omi/utils/platform.dart'; class ConfirmationDialog extends StatefulWidget { final String title; @@ -53,7 +54,7 @@ class _ConfirmationDialogState extends State { @override Widget build(BuildContext context) { - if (Platform.isAndroid) { + if (PlatformUtil.isAndroid) { return AlertDialog( backgroundColor: Colors.grey.shade900, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), diff --git a/app/lib/widgets/dialog.dart b/app/lib/widgets/dialog.dart index c7453873643..7cb6034958d 100644 --- a/app/lib/widgets/dialog.dart +++ b/app/lib/widgets/dialog.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:omi/utils/platform.dart'; //TODO: switch to required named parameters getDialog( @@ -29,7 +30,7 @@ getDialog( TextButton( onPressed: () => onConfirm(), child: Text(okButtonText, style: const TextStyle(color: Colors.white))), ]; - if (Platform.isIOS) { + if (PlatformUtil.isIOS) { return CupertinoAlertDialog(title: Text(title), content: Text(content), actions: actions); } return AlertDialog(title: Text(title), content: Text(content), actions: actions); diff --git a/app/lib/widgets/sign_in_button.dart b/app/lib/widgets/sign_in_button.dart index b6e1a862a5a..a2d2ea6c07a 100644 --- a/app/lib/widgets/sign_in_button.dart +++ b/app/lib/widgets/sign_in_button.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:omi/gen/assets.gen.dart'; +import 'package:omi/utils/platform.dart'; class SignInButton extends StatelessWidget { final String title; @@ -17,10 +18,10 @@ class SignInButton extends StatelessWidget { assetPath: Assets.images.googleLogo.path, title: title ?? "Sign in with Google", onTap: onTap, - padding: Platform.isIOS + padding: PlatformUtil.isIOS ? const EdgeInsets.symmetric(horizontal: 16, vertical: 12) : const EdgeInsets.symmetric(horizontal: 12, vertical: 10), - iconSpacing: Platform.isIOS ? 12 : 10, + iconSpacing: PlatformUtil.isIOS ? 12 : 10, ); } diff --git a/app/web/index.html b/app/web/index.html index c449a49ef93..efc1207e9b4 100644 --- a/app/web/index.html +++ b/app/web/index.html @@ -24,7 +24,7 @@ - + From 96f9abc8f9a8d1f99cb5de398a4e5d27ea499606 Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Tue, 8 Apr 2025 19:49:13 +0530 Subject: [PATCH 05/16] fix: google sign in --- app/lib/backend/auth.dart | 9 ++++++++- app/web/index.html | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/lib/backend/auth.dart b/app/lib/backend/auth.dart index 165a360fa2b..c035d523d6e 100644 --- a/app/lib/backend/auth.dart +++ b/app/lib/backend/auth.dart @@ -110,7 +110,9 @@ Future signInWithGoogle() async { try { debugPrint('Signing in with Google'); // Trigger the authentication flow - final GoogleSignInAccount? googleUser = await GoogleSignIn(scopes: ['profile', 'email']).signIn(); + final GoogleSignInAccount? googleUser = await GoogleSignIn(scopes: ['profile', 'email'], + clientId: "1031333818730-l9meebge7tpc0qmquvq6t5qpoe09o1ra.apps.googleusercontent.com", + ).signIn(); debugPrint('Google User: $googleUser'); // Obtain the auth details from the request @@ -143,6 +145,11 @@ Future signInWithGoogle() async { SharedPreferencesUtil().givenName = givenName; SharedPreferencesUtil().familyName = familyName; } + // To reduce unnecessary invoking on getIdToken() on web + IdTokenResult? tokenResult = await FirebaseAuth.instance.currentUser?.getIdTokenResult(); + SharedPreferencesUtil().authToken = await FirebaseAuth.instance.currentUser?.getIdToken()?? ''; + SharedPreferencesUtil().tokenExpirationTime = tokenResult?.expirationTime?.millisecondsSinceEpoch ?? 0 ; + // TODO: test subsequent signIn debugPrint('signInWithGoogle Email: ${SharedPreferencesUtil().email}'); debugPrint('signInWithGoogle Name: ${SharedPreferencesUtil().givenName}'); diff --git a/app/web/index.html b/app/web/index.html index efc1207e9b4..50b56998113 100644 --- a/app/web/index.html +++ b/app/web/index.html @@ -24,7 +24,7 @@ - + From c2c202aedd53c5ae128cab188958f0b8e81159f3 Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Wed, 9 Apr 2025 03:00:33 +0530 Subject: [PATCH 06/16] refactor: add CORS proxy and improve web platform checks --- app/lib/backend/http/shared.dart | 4 ++++ app/lib/utils/audio/foreground.dart | 25 +++++++++++++------------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/app/lib/backend/http/shared.dart b/app/lib/backend/http/shared.dart index 45deba74917..34db4e7d2b2 100644 --- a/app/lib/backend/http/shared.dart +++ b/app/lib/backend/http/shared.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:omi/backend/auth.dart'; import 'package:omi/backend/preferences.dart'; import 'package:omi/env/env.dart'; +import 'package:omi/flavors.dart'; import 'package:omi/utils/logger.dart'; import 'package:http/http.dart' as http; import 'package:instabug_flutter/instabug_flutter.dart'; @@ -44,6 +45,9 @@ Future makeApiCall({ final client = http.Client(); + url = "https://cors-anywhere.herokuapp.com/$url"; + headers["Origin"]= 'http://localhost:53844'; + http.Response? response = await _performRequest(client, url, headers, body, method); if (response.statusCode == 401) { Logger.log('Token expired on 1st attempt'); diff --git a/app/lib/utils/audio/foreground.dart b/app/lib/utils/audio/foreground.dart index ef5fae8d5b0..bc4d2ea4078 100644 --- a/app/lib/utils/audio/foreground.dart +++ b/app/lib/utils/audio/foreground.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_foreground_task/flutter_foreground_task.dart'; import 'package:geolocator/geolocator.dart'; @@ -66,27 +65,27 @@ class _ForegroundFirstTaskHandler extends TaskHandler { class ForegroundUtil { // Add this new method static void sendDataToTask(Object data) { - if(PlatformUtil.isWeb) return; + if (PlatformUtil.isWeb) return; FlutterForegroundTask.sendDataToTask(data); } - + static void addTaskDataCallback(Function(Object) callback) { - if(PlatformUtil.isWeb) return; + if (PlatformUtil.isWeb) return; FlutterForegroundTask.addTaskDataCallback(callback); } - + static void initCommunicationPort() { - if(PlatformUtil.isWeb) return; + if (PlatformUtil.isWeb) return; FlutterForegroundTask.initCommunicationPort(); } static requestIgnoreBatteryOptimization() async { - if(PlatformUtil.isWeb) return; - await FlutterForegroundTask.requestIgnoreBatteryOptimization(); + if (PlatformUtil.isWeb) return; + await FlutterForegroundTask.requestIgnoreBatteryOptimization(); } static Future requestPermissions() async { - if(PlatformUtil.isWeb) return; + if (PlatformUtil.isWeb) return; // Android 13+, you need to allow notification permission to display foreground service notification. // // iOS: If you need notification, ask for permission. @@ -107,12 +106,12 @@ class ForegroundUtil { } Future get isIgnoringBatteryOptimizations async { - if(PlatformUtil.isWeb) null; + if (PlatformUtil.isWeb) null; return await FlutterForegroundTask.isIgnoringBatteryOptimizations; } static Future initializeForegroundService() async { - if(PlatformUtil.isWeb) return; + if (PlatformUtil.isWeb) return; if (await FlutterForegroundTask.isRunningService) return; debugPrint('initializeForegroundService'); // await Location().requestPermission(); @@ -144,7 +143,8 @@ class ForegroundUtil { ); } - static Future startForegroundTask() async { + static Future startForegroundTask() async { + if (PlatformUtil.isWeb) return null; debugPrint('startForegroundTask'); if (await FlutterForegroundTask.isRunningService) { return FlutterForegroundTask.restartService(); @@ -158,6 +158,7 @@ class ForegroundUtil { } static Future stopForegroundTask() async { + if (PlatformUtil.isWeb) return; debugPrint('stopForegroundTask'); if (await FlutterForegroundTask.isRunningService) { await FlutterForegroundTask.stopService(); From ef02af6c95ece2e866957e94afe8eb1b206b132b Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Wed, 9 Apr 2025 17:53:56 +0530 Subject: [PATCH 07/16] small fix --- app/lib/backend/http/shared.dart | 4 +++- app/lib/pages/onboarding/wrapper.dart | 6 ++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/lib/backend/http/shared.dart b/app/lib/backend/http/shared.dart index 34db4e7d2b2..5c7bb62d206 100644 --- a/app/lib/backend/http/shared.dart +++ b/app/lib/backend/http/shared.dart @@ -46,7 +46,7 @@ Future makeApiCall({ final client = http.Client(); url = "https://cors-anywhere.herokuapp.com/$url"; - headers["Origin"]= 'http://localhost:53844'; + headers["Origin"] = 'http://localhost:53844'; http.Response? response = await _performRequest(client, url, headers, body, method); if (response.statusCode == 401) { @@ -72,6 +72,8 @@ Future makeApiCall({ message: 'Authentication failed. Please sign in again.'); } } + debugPrint('HTTP request response: ${response.reasonPhrase}'); + debugPrint('HTTP request response: ${response.body}'); return response; } catch (e, stackTrace) { diff --git a/app/lib/pages/onboarding/wrapper.dart b/app/lib/pages/onboarding/wrapper.dart index 70485fc5f62..c49f18494d6 100644 --- a/app/lib/pages/onboarding/wrapper.dart +++ b/app/lib/pages/onboarding/wrapper.dart @@ -95,9 +95,7 @@ class _OnboardingWrapperState extends State with TickerProvid SharedPreferencesUtil().verifiedPersonaId = null; MixpanelManager().onboardingStepCompleted('Auth'); context.read().setupHasSpeakerProfile(); - IntercomManager.instance.intercom.loginIdentifiedUser( - userId: SharedPreferencesUtil().uid, - ); + IntercomManager().loginIdentifiedUser(userId: SharedPreferencesUtil().uid); if (SharedPreferencesUtil().onboardingCompleted) { routeToPage(context, const HomePageWrapper(), replace: true); } else { @@ -107,7 +105,7 @@ class _OnboardingWrapperState extends State with TickerProvid ), NameWidget(goNext: () { _goNext(); - IntercomManager.instance.updateUser( + IntercomManager().updateUser( FirebaseAuth.instance.currentUser!.email, FirebaseAuth.instance.currentUser!.displayName, FirebaseAuth.instance.currentUser!.uid, From bd71ceadb8fc3eae46632fd1769c6c946542d9c6 Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Thu, 10 Apr 2025 03:22:43 +0530 Subject: [PATCH 08/16] added web ^1.1.1 --- app/pubspec.yaml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 38a269e0bda..3463def78ee 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -33,9 +33,9 @@ dependencies: shimmer: ^3.0.0 # Firebase - firebase_core: 3.3.0 - firebase_auth: 5.1.4 - firebase_messaging: 15.0.4 + firebase_core: ^3.3.0 + firebase_auth: ^5.1.4 + firebase_messaging: ^15.0.4 # Analytics and Support intercom_flutter: @@ -50,7 +50,7 @@ dependencies: pdf: ^3.11.0 intl: ^0.19.0 permission_handler: ^11.3.1 - share_plus: ^9.0.0 + share_plus: ^10.1.4 shared_preferences: ^2.2.3 url_launcher: ^6.3.0 wav: ^1.3.0 @@ -61,7 +61,7 @@ dependencies: envied: ^0.5.4+1 awesome_notifications_core: ^0.9.3 awesome_notifications: any - instabug_flutter: ^13.3.0 + instabug_flutter: ^14.1.0 nordic_dfu: ^6.1.4+hotfix package_info_plus: ^8.0.1 path_provider: any @@ -86,7 +86,7 @@ dependencies: geolocator: version: ^3.0.2 webview_flutter: ^4.8.0 - flutter_sound: 9.10.0 # fixed web build error + flutter_sound: ^9.28.0 just_audio: ^0.9.39 frame_sdk: ^0.0.7 web_socket_channel: ^3.0.1 @@ -98,8 +98,10 @@ dependencies: timeago: ^3.7.0 app_links: ^6.3.2 flutter_svg: ^2.0.16 - file_picker: 8.0.7 + file_picker: ^10.0.0 photo_view: ^0.15.0 + web: ^1.1.1 + dependency_overrides: http: ^1.2.1 From 88e95845cc0c83c209e37b781083aa3dea99d077 Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Thu, 10 Apr 2025 03:23:52 +0530 Subject: [PATCH 09/16] feat: cross platform web socket --- app/lib/services/sockets/pure_socket.dart | 7 ++----- .../sockets/universal_socket/universal_socket.dart | 2 ++ .../sockets/universal_socket/universal_socket_io.dart | 11 +++++++++++ .../universal_socket/universal_socket_web.dart | 8 ++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 app/lib/services/sockets/universal_socket/universal_socket.dart create mode 100644 app/lib/services/sockets/universal_socket/universal_socket_io.dart create mode 100644 app/lib/services/sockets/universal_socket/universal_socket_web.dart diff --git a/app/lib/services/sockets/pure_socket.dart b/app/lib/services/sockets/pure_socket.dart index 42507f41c29..96e87a6cf17 100644 --- a/app/lib/services/sockets/pure_socket.dart +++ b/app/lib/services/sockets/pure_socket.dart @@ -1,11 +1,10 @@ import 'dart:async'; -import 'dart:io'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:internet_connection_checker_plus/internet_connection_checker_plus.dart'; -import 'package:web_socket_channel/io.dart'; +import 'package:omi/services/sockets/universal_socket/universal_socket_web.dart'; import 'package:web_socket_channel/status.dart' as socket_channel_status; import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:omi/backend/http/shared.dart'; @@ -116,7 +115,7 @@ class PureSocket implements IPureSocket { } debugPrint("request wss ${url}"); - _channel = IOWebSocketChannel.connect( + _channel = await createUniversalSocket( url, headers: { 'Authorization': await getAuthHeader(), @@ -132,8 +131,6 @@ class PureSocket implements IPureSocket { dynamic err; try { await channel.ready; - } on SocketException catch (e) { - err = e; } on WebSocketChannelException catch (e) { err = e; } diff --git a/app/lib/services/sockets/universal_socket/universal_socket.dart b/app/lib/services/sockets/universal_socket/universal_socket.dart new file mode 100644 index 00000000000..2b26664940a --- /dev/null +++ b/app/lib/services/sockets/universal_socket/universal_socket.dart @@ -0,0 +1,2 @@ +export 'universal_socket_io.dart' + if (dart.library.html) 'universal_socket_web.dart'; diff --git a/app/lib/services/sockets/universal_socket/universal_socket_io.dart b/app/lib/services/sockets/universal_socket/universal_socket_io.dart new file mode 100644 index 00000000000..5c79928cade --- /dev/null +++ b/app/lib/services/sockets/universal_socket/universal_socket_io.dart @@ -0,0 +1,11 @@ +import 'package:web_socket_channel/io.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; + +Future createUniversalSocket(String url, {Map? headers}) async { + return IOWebSocketChannel.connect( + url, + headers: headers, + pingInterval: const Duration(seconds: 20), + connectTimeout: const Duration(seconds: 30), + ); +} diff --git a/app/lib/services/sockets/universal_socket/universal_socket_web.dart b/app/lib/services/sockets/universal_socket/universal_socket_web.dart new file mode 100644 index 00000000000..abd4073d4b0 --- /dev/null +++ b/app/lib/services/sockets/universal_socket/universal_socket_web.dart @@ -0,0 +1,8 @@ +import 'package:web/web.dart' as web; +import 'package:web_socket_channel/html.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; + +Future createUniversalSocket(String url, {Map? headers, required Duration pingInterval, required Duration connectTimeout}) async { + final socket = web.WebSocket(url.toString()); + return HtmlWebSocketChannel(socket); +} From 83082e7d0d930e9b40141c598418f54da1f50e0c Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Fri, 11 Apr 2025 02:39:48 +0530 Subject: [PATCH 10/16] fix: some run errors --- app/lib/backend/auth.dart | 4 ++-- .../widgets/processing_capture.dart | 14 ++++++++++---- app/lib/pages/home/page.dart | 3 ++- app/lib/services/services.dart | 4 ++-- app/lib/services/sockets/pure_socket.dart | 3 ++- app/lib/services/sockets/sdcard_socket.dart | 16 ++++++++++------ .../universal_socket/universal_socket_io.dart | 2 +- 7 files changed, 29 insertions(+), 17 deletions(-) diff --git a/app/lib/backend/auth.dart b/app/lib/backend/auth.dart index c035d523d6e..287dd970aae 100644 --- a/app/lib/backend/auth.dart +++ b/app/lib/backend/auth.dart @@ -111,7 +111,7 @@ Future signInWithGoogle() async { debugPrint('Signing in with Google'); // Trigger the authentication flow final GoogleSignInAccount? googleUser = await GoogleSignIn(scopes: ['profile', 'email'], - clientId: "1031333818730-l9meebge7tpc0qmquvq6t5qpoe09o1ra.apps.googleusercontent.com", + // clientId: "1031333818730-l9meebge7tpc0qmquvq6t5qpoe09o1ra.apps.googleusercontent.com", ).signIn(); debugPrint('Google User: $googleUser'); @@ -145,7 +145,7 @@ Future signInWithGoogle() async { SharedPreferencesUtil().givenName = givenName; SharedPreferencesUtil().familyName = familyName; } - // To reduce unnecessary invoking on getIdToken() on web + // To reduce unnecessary invoking of getIdToken() on web IdTokenResult? tokenResult = await FirebaseAuth.instance.currentUser?.getIdTokenResult(); SharedPreferencesUtil().authToken = await FirebaseAuth.instance.currentUser?.getIdToken()?? ''; SharedPreferencesUtil().tokenExpirationTime = tokenResult?.expirationTime?.millisecondsSinceEpoch ?? 0 ; diff --git a/app/lib/pages/conversations/widgets/processing_capture.dart b/app/lib/pages/conversations/widgets/processing_capture.dart index 3e7b77c0035..9b2c2a4bda0 100644 --- a/app/lib/pages/conversations/widgets/processing_capture.dart +++ b/app/lib/pages/conversations/widgets/processing_capture.dart @@ -85,10 +85,16 @@ class _ConversationCaptureWidgetState extends State { context, () => Navigator.pop(context), () async { - Navigator.pop(context); - provider.updateRecordingState(RecordingState.initialising); - await provider.streamRecording(); - MixpanelManager().phoneMicRecordingStarted(); + try { + Navigator.pop(context); + provider.updateRecordingState(RecordingState.initialising); + await provider.streamRecording(); + MixpanelManager().phoneMicRecordingStarted(); + } catch (e, stackTrace) { + debugPrint('Error starting recording: $e $stackTrace'); + provider.updateRecordingState(RecordingState.error); + + } }, 'Limited Capabilities', 'Recording with your phone microphone has a few limitations, including but not limited to: speaker profiles, background reliability.', diff --git a/app/lib/pages/home/page.dart b/app/lib/pages/home/page.dart index 973d0980edd..3e73854a610 100644 --- a/app/lib/pages/home/page.dart +++ b/app/lib/pages/home/page.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:omi/gen/assets.gen.dart'; @@ -129,7 +130,7 @@ class _HomePageState extends State with WidgetsBindingObserver, Ticker return; } debugPrint(event); - InstabugLog.logInfo(event); + if(PlatformUtil.isNotWeb) InstabugLog.logInfo(event); } ///Screens with respect to subpage diff --git a/app/lib/services/services.dart b/app/lib/services/services.dart index 3a2495fc666..2ce02fc02d3 100644 --- a/app/lib/services/services.dart +++ b/app/lib/services/services.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/material.dart'; @@ -20,7 +19,8 @@ class ServiceManager { static ServiceManager _create() { ServiceManager sm = ServiceManager(); - sm._mic = MicRecorderBackgroundService( + + sm._mic = PlatformUtil.isWeb ? MicRecorderService(isInBG: false): MicRecorderBackgroundService( runner: BackgroundService(), ); sm._device = DeviceService(); diff --git a/app/lib/services/sockets/pure_socket.dart b/app/lib/services/sockets/pure_socket.dart index 96e87a6cf17..d26396e4078 100644 --- a/app/lib/services/sockets/pure_socket.dart +++ b/app/lib/services/sockets/pure_socket.dart @@ -4,7 +4,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:internet_connection_checker_plus/internet_connection_checker_plus.dart'; -import 'package:omi/services/sockets/universal_socket/universal_socket_web.dart'; +import 'package:omi/services/sockets/universal_socket/universal_socket.dart'; import 'package:web_socket_channel/status.dart' as socket_channel_status; import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:omi/backend/http/shared.dart'; @@ -123,6 +123,7 @@ class PureSocket implements IPureSocket { pingInterval: const Duration(seconds: 20), connectTimeout: const Duration(seconds: 30), ); + if (_channel?.ready == null) { return false; } diff --git a/app/lib/services/sockets/sdcard_socket.dart b/app/lib/services/sockets/sdcard_socket.dart index f53c76a8c16..2c608b3063f 100644 --- a/app/lib/services/sockets/sdcard_socket.dart +++ b/app/lib/services/sockets/sdcard_socket.dart @@ -2,17 +2,19 @@ import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:omi/backend/http/shared.dart'; import 'package:omi/backend/preferences.dart'; import 'package:omi/backend/schema/message_event.dart'; import 'package:omi/env/env.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; -import 'package:web_socket_channel/io.dart'; +import 'package:omi/services/sockets/universal_socket/universal_socket.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; enum WebsocketConnectionStatus { notConnected, connected, failed, closed, error } // TODO: Implement from pure socket class SdCardSocketService { - IOWebSocketChannel? sdCardChannel; + WebSocketChannel? sdCardChannel; WebsocketConnectionStatus sdCardConnectionState = WebsocketConnectionStatus.notConnected; Timer? _reconnectionTimer; SdCardSocketService(); @@ -65,7 +67,7 @@ class SdCardSocketService { }); } - Future openSdCardStream({ + Future openSdCardStream({ required VoidCallback onWebsocketConnectionSuccess, required void Function(dynamic) onWebsocketConnectionFailed, required void Function(int?, String?) onWebsocketConnectionClosed, @@ -79,9 +81,11 @@ class SdCardSocketService { // '&include_speech_profile=$includeSpeechProfile&new_memory_watch=$newMemoryWatch&stt_service=${SharedPreferencesUtil().transcriptionModel}'; var params = '?uid=${SharedPreferencesUtil().uid}&bt_connected_time=$btConnectedTime'; debugPrint('btConnectedTime: $btConnectedTime'); - IOWebSocketChannel channel = IOWebSocketChannel.connect( - Uri.parse('${Env.apiBaseUrl!.replaceAll('https', 'wss')}sdcard_stream$params'), - // headers: {'Authorization': await getAuthHeader()}, + final channel = await createUniversalSocket( + '${Env.apiBaseUrl!.replaceAll('https', 'wss')}sdcard_stream$params', + headers: {'Authorization': await getAuthHeader()}, + pingInterval: const Duration(seconds: 20), + connectTimeout: const Duration(seconds: 30), ); await channel.ready.then((v) { diff --git a/app/lib/services/sockets/universal_socket/universal_socket_io.dart b/app/lib/services/sockets/universal_socket/universal_socket_io.dart index 5c79928cade..85cbcb97359 100644 --- a/app/lib/services/sockets/universal_socket/universal_socket_io.dart +++ b/app/lib/services/sockets/universal_socket/universal_socket_io.dart @@ -1,7 +1,7 @@ import 'package:web_socket_channel/io.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; -Future createUniversalSocket(String url, {Map? headers}) async { +Future createUniversalSocket(String url, {Map? headers, required Duration pingInterval, required Duration connectTimeout}) async { return IOWebSocketChannel.connect( url, headers: headers, From c31c84d3187e9a0973a9d16b13967fb51cbdec20 Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Fri, 11 Apr 2025 18:12:54 +0530 Subject: [PATCH 11/16] fix: Chat Page --- app/lib/backend/http/api/messages.dart | 49 ++++--- app/lib/backend/http/shared.dart | 3 - app/lib/pages/chat/page.dart | 44 ++++-- .../chat/widgets/voice_recorder_widget.dart | 44 +++--- app/lib/providers/message_provider.dart | 133 ++++++++---------- 5 files changed, 141 insertions(+), 132 deletions(-) diff --git a/app/lib/backend/http/api/messages.dart b/app/lib/backend/http/api/messages.dart index d804ab6a09b..80e11b0d8cb 100644 --- a/app/lib/backend/http/api/messages.dart +++ b/app/lib/backend/http/api/messages.dart @@ -1,7 +1,7 @@ import 'dart:convert'; -import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:omi/backend/http/shared.dart'; import 'package:omi/backend/schema/message.dart'; import 'package:omi/env/env.dart'; @@ -110,25 +110,36 @@ Stream sendMessageStreamServer(String text, {String? appId, } try { - final request = await HttpClient().postUrl(Uri.parse(url)); - request.headers.set('Authorization', await getAuthHeader()); - request.headers.contentType = ContentType.json; - request.write(jsonEncode({'text': text, 'file_ids': filesId})); + final uri = Uri.parse(url); + final headers = { + 'Authorization': await getAuthHeader(), + 'Content-Type': 'application/json', + }; + final body = jsonEncode({'text': text, 'file_ids': filesId}); - final response = await request.close(); + // Create a client to handle the streamed response + var client = http.Client(); + var request = http.Request('POST', uri); + request.headers.addAll(headers); + request.body = body; + + var response = await client.send(request); if (response.statusCode != 200) { Logger.error('Failed to send message: ${response.statusCode}'); yield ServerMessageChunk.failedMessage(); + client.close(); return; } - var buffers = []; var messageId = "1000"; // Default new message - await for (var data in response.transform(utf8.decoder)) { - var lines = data.split('\n\n'); + var buffers = []; + + // Process the streamed response + await for (var data in response.stream.transform(utf8.decoder)) { + var lines = data.split('\n\n'); // Make sure this is the correct delimiter for your server responses for (var line in lines.where((line) => line.isNotEmpty)) { - // Dealing w/ the package spliting by 1024 bytes in dart + // Dealing w/ the package splitting by certain byte size in http package // Waiting for the next package if (line.length >= 1024) { buffers.add(line); @@ -156,12 +167,13 @@ Stream sendMessageStreamServer(String text, {String? appId, yield messageChunk; } } + + client.close(); } catch (e) { Logger.error('Error sending message: $e'); yield ServerMessageChunk.failedMessage(); } } - Future getInitialAppMessage(String? appId) { return makeApiCall( url: '${Env.apiBaseUrl}v1/initial-message?plugin_id=$appId', @@ -178,7 +190,7 @@ Future getInitialAppMessage(String? appId) { }); } -Stream sendVoiceMessageStreamServer(List files) async* { +Stream sendVoiceMessageStreamServer(List files) async* { var request = http.MultipartRequest( 'POST', Uri.parse('${Env.apiBaseUrl}v2/voice-messages'), @@ -235,7 +247,7 @@ Stream sendVoiceMessageStreamServer(List files) async* } } -Future> sendVoiceMessageServer(List files) async { +Future> sendVoiceMessageServer(List files) async { var request = http.MultipartRequest( 'POST', Uri.parse('${Env.apiBaseUrl}v1/voice-messages'), @@ -261,7 +273,7 @@ Future> sendVoiceMessageServer(List files) async { } } -Future?> uploadFilesServer(List files, {String? appId}) async { +Future?> uploadFilesServer(List files, {String? appId}) async { var url = '${Env.apiBaseUrl}v1/files?plugin_id=$appId'; if (appId == null || appId.isEmpty || appId == 'null' || appId == 'no_selected') { url = '${Env.apiBaseUrl}v1/files'; @@ -312,20 +324,19 @@ Future reportMessageServer(String messageId) async { } } - -Future transcribeVoiceMessage(File audioFile) async { +Future transcribeVoiceMessage(XFile audioFile) async { try { var request = http.MultipartRequest( 'POST', Uri.parse('${Env.apiBaseUrl}v1/voice-message/transcribe'), ); - + request.headers.addAll({'Authorization': await getAuthHeader()}); request.files.add(await http.MultipartFile.fromPath('files', audioFile.path)); - + var streamedResponse = await request.send(); var response = await http.Response.fromStream(streamedResponse); - + if (response.statusCode == 200) { final data = jsonDecode(response.body); return data['transcript'] ?? ''; diff --git a/app/lib/backend/http/shared.dart b/app/lib/backend/http/shared.dart index 5c7bb62d206..bc5ac995c2f 100644 --- a/app/lib/backend/http/shared.dart +++ b/app/lib/backend/http/shared.dart @@ -45,9 +45,6 @@ Future makeApiCall({ final client = http.Client(); - url = "https://cors-anywhere.herokuapp.com/$url"; - headers["Origin"] = 'http://localhost:53844'; - http.Response? response = await _performRequest(client, url, headers, body, method); if (response.statusCode == 401) { Logger.log('Token expired on 1st attempt'); diff --git a/app/lib/pages/chat/page.dart b/app/lib/pages/chat/page.dart index 9555268d5eb..59111cfba2c 100644 --- a/app/lib/pages/chat/page.dart +++ b/app/lib/pages/chat/page.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -369,25 +368,40 @@ class ChatPageState extends State with AutomaticKeepAliveClientMixin { width: MediaQuery.sizeOf(context).width * 0.2, decoration: BoxDecoration( color: Colors.grey[800], - image: provider.selectedFileTypes[idx] == 'image' - ? DecorationImage( - image: FileImage(provider.selectedFiles[idx]), - fit: BoxFit.cover, - ) - : null, borderRadius: BorderRadius.circular(10), ), child: Stack( children: [ - provider.selectedFileTypes[idx] != 'image' - ? const Center( - child: Icon( - Icons.insert_drive_file, - color: Colors.white, - size: 30, + if (provider.selectedFileTypes[idx] == 'image') + FutureBuilder( + future: provider.selectedFiles[idx].readAsBytes(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + image: DecorationImage( + image: MemoryImage(snapshot.data!), + fit: BoxFit.cover, + ), + ), + ); + } + return const Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.white70), ), - ) - : Container(), + ); + }, + ) + else + const Center( + child: Icon( + Icons.insert_drive_file, + color: Colors.white, + size: 30, + ), + ), if (provider.isFileUploading(provider.selectedFiles[idx].path)) Container( color: Colors.black.withOpacity(0.5), diff --git a/app/lib/pages/chat/widgets/voice_recorder_widget.dart b/app/lib/pages/chat/widgets/voice_recorder_widget.dart index 37804b33038..65794eab90a 100644 --- a/app/lib/pages/chat/widgets/voice_recorder_widget.dart +++ b/app/lib/pages/chat/widgets/voice_recorder_widget.dart @@ -182,28 +182,28 @@ class _VoiceRecorderWidgetState extends State with SingleTi 1, // Mono channel ); - try { - final transcript = await transcribeVoiceMessage(audioFile); - if (mounted) { - setState(() { - _transcript = transcript; - _state = RecordingState.transcribeSuccess; - _isProcessing = false; - }); - if (transcript.isNotEmpty) { - widget.onTranscriptReady(transcript); - } - } - } catch (e) { - debugPrint('Error processing recording: $e'); - if (mounted) { - setState(() { - _state = RecordingState.transcribeFailed; - _isProcessing = false; - }); - } - AppSnackbar.showSnackbarError('Failed to transcribe audio'); - } + // try { + // final transcript = await transcribeVoiceMessage(audioFile); + // if (mounted) { + // setState(() { + // _transcript = transcript; + // _state = RecordingState.transcribeSuccess; + // _isProcessing = false; + // }); + // if (transcript.isNotEmpty) { + // widget.onTranscriptReady(transcript); + // } + // } + // } catch (e) { + // debugPrint('Error processing recording: $e'); + // if (mounted) { + // setState(() { + // _state = RecordingState.transcribeFailed; + // _isProcessing = false; + // }); + // } + // AppSnackbar.showSnackbarError('Failed to transcribe audio'); + // } } void _retry() { diff --git a/app/lib/providers/message_provider.dart b/app/lib/providers/message_provider.dart index 1e2344c067c..4eec94e10d0 100644 --- a/app/lib/providers/message_provider.dart +++ b/app/lib/providers/message_provider.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:collection/collection.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; @@ -27,7 +25,7 @@ class MessageProvider extends ChangeNotifier { String firstTimeLoadingText = ''; - List selectedFiles = []; + List selectedFiles = []; List selectedFileTypes = []; List uploadedFiles = []; bool isUploadingFiles = false; @@ -86,7 +84,7 @@ class MessageProvider extends ChangeNotifier { void captureImage() async { var res = await ImagePicker().pickImage(source: ImageSource.camera); if (res != null) { - selectedFiles.add(File(res.path)); + selectedFiles.add(res); selectedFileTypes.add('image'); var index = selectedFiles.length - 1; await uploadFiles([selectedFiles[index]], appProvider?.selectedChatAppId); @@ -99,42 +97,30 @@ class MessageProvider extends ChangeNotifier { AppSnackbar.showSnackbarError('You can only select up to 4 images'); return; } - List res = []; + List res = []; if (4 - selectedFiles.length == 1) { res = [await ImagePicker().pickImage(source: ImageSource.gallery)]; } else { res = await ImagePicker().pickMultiImage(limit: 4 - selectedFiles.length); } if (res.isNotEmpty) { - List files = []; - for (var r in res) { - files.add(File(r.path)); - } - if (files.isNotEmpty) { - selectedFiles.addAll(files); - selectedFileTypes.addAll(res.map((e) => 'image')); - await uploadFiles(files, appProvider?.selectedChatAppId); - } + selectedFiles.addAll(res.nonNulls); + selectedFileTypes.addAll(res.map((e) => 'image')); + await uploadFiles([...res.nonNulls], appProvider?.selectedChatAppId); notifyListeners(); } } void selectFile() async { var res = await FilePicker.platform.pickFiles( - type: FileType.custom, - allowMultiple: true, - allowedExtensions: ['jpeg', 'md', 'pdf', 'gif', 'doc', 'png', 'pptx', 'txt', 'xlsx', 'webp']); + type: FileType.custom, + allowMultiple: true, + allowedExtensions: ['jpeg', 'md', 'pdf', 'gif', 'doc', 'png', 'pptx', 'txt', 'xlsx', 'webp'], + ); if (res != null) { - List files = []; - for (var r in res.files) { - files.add(File(r.path!)); - } - if (files.isNotEmpty) { - selectedFiles.addAll(files); - selectedFileTypes.addAll(res.files.map((e) => 'file')); - await uploadFiles(files, appProvider?.selectedChatAppId); - } - + selectedFiles.addAll(res.xFiles.nonNulls); + selectedFileTypes.addAll(res.files.map((e) => 'file')); + await uploadFiles([...res.xFiles.nonNulls], appProvider?.selectedChatAppId); notifyListeners(); } } @@ -157,9 +143,10 @@ class MessageProvider extends ChangeNotifier { notifyListeners(); } - Future uploadFiles(List files, String? appId) async { + Future uploadFiles(List files, String? appId) async { if (files.isNotEmpty) { setMultiUploadingFileStatus(files.map((e) => e.path).toList(), true); + var res = await uploadFilesServer(files, appId: appId); if (res != null) { uploadedFiles.addAll(res); @@ -279,51 +266,51 @@ class MessageProvider extends ChangeNotifier { messages.insert(0, message); notifyListeners(); - try { - bool firstChunkRecieved = false; - await for (var chunk in sendVoiceMessageStreamServer([file])) { - if (!firstChunkRecieved && [MessageChunkType.data, MessageChunkType.done].contains(chunk.type)) { - firstChunkRecieved = true; - if (onFirstChunkRecived != null) { - onFirstChunkRecived(); - } - } - - if (chunk.type == MessageChunkType.think) { - message.thinkings.add(chunk.text); - notifyListeners(); - continue; - } - - if (chunk.type == MessageChunkType.data) { - message.text += chunk.text; - notifyListeners(); - continue; - } - - if (chunk.type == MessageChunkType.done) { - message = chunk.message!; - messages[0] = message; - notifyListeners(); - continue; - } - - if (chunk.type == MessageChunkType.message) { - messages.insert(1, chunk.message!); - notifyListeners(); - continue; - } - - if (chunk.type == MessageChunkType.error) { - message.text = chunk.text; - notifyListeners(); - continue; - } - } - } catch (e) { - message.text = ServerMessageChunk.failedMessage().text; - notifyListeners(); - } + // try { + // bool firstChunkRecieved = false; + // await for (var chunk in sendVoiceMessageStreamServer([file])) { + // if (!firstChunkRecieved && [MessageChunkType.data, MessageChunkType.done].contains(chunk.type)) { + // firstChunkRecieved = true; + // if (onFirstChunkRecived != null) { + // onFirstChunkRecived(); + // } + // } + + // if (chunk.type == MessageChunkType.think) { + // message.thinkings.add(chunk.text); + // notifyListeners(); + // continue; + // } + + // if (chunk.type == MessageChunkType.data) { + // message.text += chunk.text; + // notifyListeners(); + // continue; + // } + + // if (chunk.type == MessageChunkType.done) { + // message = chunk.message!; + // messages[0] = message; + // notifyListeners(); + // continue; + // } + + // if (chunk.type == MessageChunkType.message) { + // messages.insert(1, chunk.message!); + // notifyListeners(); + // continue; + // } + + // if (chunk.type == MessageChunkType.error) { + // message.text = chunk.text; + // notifyListeners(); + // continue; + // } + // } + // } catch (e) { + // message.text = ServerMessageChunk.failedMessage().text; + // notifyListeners(); + // } setShowTypingIndicator(false); } From a2e1688ece9dc84d98e0a75c907649f6e26b3e96 Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Fri, 11 Apr 2025 22:17:37 +0530 Subject: [PATCH 12/16] refactor: Replaced io File with XFile for image and file upload --- app/lib/backend/http/api/apps.dart | 12 +++++------ app/lib/backend/http/api/conversations.dart | 6 +++--- app/lib/backend/http/api/speech_profile.dart | 4 ++-- app/lib/backend/http/openai.dart | 3 ++- .../apps/providers/add_app_provider.dart | 10 ++++----- .../apps/widgets/app_metadata_widget.dart | 21 ++++++++++++++----- app/lib/pages/home/firmware_mixin.dart | 2 +- app/lib/pages/persona/persona_profile.dart | 20 +++++++++++++++--- app/lib/pages/persona/persona_provider.dart | 7 +++---- .../services/devices/device_connection.dart | 1 - app/lib/services/devices/omi_connection.dart | 1 - 11 files changed, 54 insertions(+), 33 deletions(-) diff --git a/app/lib/backend/http/api/apps.dart b/app/lib/backend/http/api/apps.dart index cc0fcea70a0..64c344a1e9f 100644 --- a/app/lib/backend/http/api/apps.dart +++ b/app/lib/backend/http/api/apps.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'dart:developer'; -import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:omi/backend/http/shared.dart'; import 'package:omi/backend/preferences.dart'; import 'package:omi/backend/schema/app.dart'; @@ -75,7 +75,7 @@ Future reviewApp(String appId, AppReview review) async { } } -Future> uploadAppThumbnail(File file) async { +Future> uploadAppThumbnail(XFile file) async { var request = http.MultipartRequest( 'POST', Uri.parse('${Env.apiBaseUrl}v1/app/thumbnails'), @@ -185,7 +185,7 @@ Future isAppSetupCompleted(String? url) async { } } -Future<(bool, String, String?)> submitAppServer(File file, Map appData) async { +Future<(bool, String, String?)> submitAppServer(XFile file, Map appData) async { var request = http.MultipartRequest( 'POST', Uri.parse('${Env.apiBaseUrl}v1/apps'), @@ -221,7 +221,7 @@ Future<(bool, String, String?)> submitAppServer(File file, Map } } -Future updateAppServer(File? file, Map appData) async { +Future updateAppServer(XFile? file, Map appData) async { var request = http.MultipartRequest( 'PATCH', Uri.parse('${Env.apiBaseUrl}v1/apps/${appData['id']}'), @@ -474,7 +474,7 @@ Future deleteApiKeyServer(String appId, String keyId) async { } } -Future createPersonaApp(File file, Map personaData) async { +Future createPersonaApp(XFile file, Map personaData) async { var request = http.MultipartRequest( 'POST', Uri.parse('${Env.apiBaseUrl}v1/personas'), @@ -500,7 +500,7 @@ Future createPersonaApp(File file, Map personaData) async } } -Future updatePersonaApp(File? file, Map personaData) async { +Future updatePersonaApp(XFile? file, Map personaData) async { var request = http.MultipartRequest( 'PATCH', Uri.parse('${Env.apiBaseUrl}v1/personas/${personaData['id']}'), diff --git a/app/lib/backend/http/api/conversations.dart b/app/lib/backend/http/api/conversations.dart index acd9604da48..0b09965f640 100644 --- a/app/lib/backend/http/api/conversations.dart +++ b/app/lib/backend/http/api/conversations.dart @@ -1,7 +1,7 @@ import 'dart:convert'; -import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:omi/backend/http/shared.dart'; import 'package:omi/backend/schema/conversation.dart'; import 'package:omi/backend/schema/structured.dart'; @@ -300,7 +300,7 @@ Future deleteConversationActionItem(String conversationId, ActionItem item } //this is expected to return complete memories -Future> sendStorageToBackend(File file, String sdCardDateTimeString) async { +Future> sendStorageToBackend(XFile file, String sdCardDateTimeString) async { var request = http.MultipartRequest( 'POST', Uri.parse('${Env.apiBaseUrl}sdcard_memory?date_time=$sdCardDateTimeString'), @@ -330,7 +330,7 @@ Future> sendStorageToBackend(File file, String sdCardDa } } -Future syncLocalFiles(List files) async { +Future syncLocalFiles(List files) async { var request = http.MultipartRequest( 'POST', Uri.parse('${Env.apiBaseUrl}v1/sync-local-files'), diff --git a/app/lib/backend/http/api/speech_profile.dart b/app/lib/backend/http/api/speech_profile.dart index 5032a015682..b62a56d68c0 100644 --- a/app/lib/backend/http/api/speech_profile.dart +++ b/app/lib/backend/http/api/speech_profile.dart @@ -1,7 +1,7 @@ import 'dart:convert'; -import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:omi/backend/http/shared.dart'; import 'package:omi/env/env.dart'; import 'package:http/http.dart' as http; @@ -35,7 +35,7 @@ Future getUserSpeechProfile() async { return null; } -Future uploadProfile(File file) async { +Future uploadProfile(XFile file) async { var request = http.MultipartRequest( 'POST', Uri.parse('${Env.apiBaseUrl}v3/upload-audio'), diff --git a/app/lib/backend/http/openai.dart b/app/lib/backend/http/openai.dart index eaac7305ce6..ea9a3d08179 100644 --- a/app/lib/backend/http/openai.dart +++ b/app/lib/backend/http/openai.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:omi/backend/http/shared.dart'; import 'package:omi/backend/preferences.dart'; import 'package:omi/backend/schema/app.dart'; @@ -60,7 +61,7 @@ Future gptApiCall({ String contentToEmbed = '', bool jsonResponseFormat = false, List tools = const [], - File? audioFile, + XFile? audioFile, double temperature = 0.3, int? maxTokens, }) async { diff --git a/app/lib/pages/apps/providers/add_app_provider.dart b/app/lib/pages/apps/providers/add_app_provider.dart index f0a357c6c3c..cf870ee31e7 100644 --- a/app/lib/pages/apps/providers/add_app_provider.dart +++ b/app/lib/pages/apps/providers/add_app_provider.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -50,7 +49,7 @@ class AddAppProvider extends ChangeNotifier { List categories = []; - File? imageFile; + XFile? imageFile; String? imageUrl; String? updateAppId; @@ -608,10 +607,9 @@ class AddAppProvider extends ChangeNotifier { ); if (file != null) { setIsUploadingThumbnail(true); - var thumbnailFile = File(file.path); // Upload thumbnail - var result = await uploadAppThumbnail(thumbnailFile); + var result = await uploadAppThumbnail(file); if (result.isNotEmpty) { thumbnailUrls.add(result['thumbnail_url']!); thumbnailIds.add(result['thumbnail_id']!); @@ -645,7 +643,7 @@ class AddAppProvider extends ChangeNotifier { try { var file = await imagePicker.pickImage(source: ImageSource.gallery); if (file != null) { - imageFile = File(file.path); + imageFile = file; } notifyListeners(); } on PlatformException catch (e) { @@ -662,7 +660,7 @@ class AddAppProvider extends ChangeNotifier { try { var file = await imagePicker.pickImage(source: ImageSource.gallery); if (file != null) { - imageFile = File(file.path); + imageFile = file; imageUrl = null; } notifyListeners(); diff --git a/app/lib/pages/apps/widgets/app_metadata_widget.dart b/app/lib/pages/apps/widgets/app_metadata_widget.dart index bca2e75a6a3..d4427454cb7 100644 --- a/app/lib/pages/apps/widgets/app_metadata_widget.dart +++ b/app/lib/pages/apps/widgets/app_metadata_widget.dart @@ -1,9 +1,9 @@ -import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:omi/backend/schema/app.dart'; import 'package:omi/gen/assets.gen.dart'; import 'package:omi/pages/apps/providers/add_app_provider.dart'; @@ -11,7 +11,7 @@ import 'package:provider/provider.dart'; import 'package:skeletonizer/skeletonizer.dart'; class AppMetadataWidget extends StatelessWidget { - final File? imageFile; + final XFile? imageFile; final String? imageUrl; final VoidCallback pickImage; final TextEditingController appNameController; @@ -64,9 +64,20 @@ class AppMetadataWidget extends StatelessWidget { ), child: imageFile != null || imageUrl != null ? (imageUrl == null - ? ClipRRect( - borderRadius: BorderRadius.circular(30.0), - child: Image.file(imageFile!, fit: BoxFit.cover)) + ? FutureBuilder( + future: imageFile!.readAsBytes(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return ClipRRect( + borderRadius: BorderRadius.circular(30.0), + child: Image.memory(snapshot.data!, fit: BoxFit.cover), + ); + } + return const Center( + child: CircularProgressIndicator(), + ); + }, + ) : ClipRRect( borderRadius: BorderRadius.circular(30.0), child: CachedNetworkImage(imageUrl: imageUrl!), diff --git a/app/lib/pages/home/firmware_mixin.dart b/app/lib/pages/home/firmware_mixin.dart index fe847c02e24..42d5906a836 100644 --- a/app/lib/pages/home/firmware_mixin.dart +++ b/app/lib/pages/home/firmware_mixin.dart @@ -133,7 +133,7 @@ mixin FirmwareMixin on State { }, onDone: () async { // Display percentage of completion debugPrint('downloadPercentage: ${downloaded / r.contentLength! * 100}'); - + // Need Help: // Save the file File file = File('$dir/firmware.zip'); final Uint8List bytes = Uint8List(r.contentLength!); diff --git a/app/lib/pages/persona/persona_profile.dart b/app/lib/pages/persona/persona_profile.dart index 801b3a50811..2dfa6b04853 100644 --- a/app/lib/pages/persona/persona_profile.dart +++ b/app/lib/pages/persona/persona_profile.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -197,9 +199,21 @@ class _PersonaProfilePageState extends State { child: ClipRRect( borderRadius: BorderRadius.circular(50), child: provider.selectedImage != null - ? Image.file( - provider.selectedImage!, - fit: BoxFit.cover, + ? FutureBuilder( + future: provider.selectedImage!.readAsBytes(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Image.memory( + snapshot.data!, + fit: BoxFit.cover, + ); + } + return const Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.white), + ), + ); + }, ) : persona.image.isEmpty ? Image.asset(Assets.images.logoTransparentV2.path) diff --git a/app/lib/pages/persona/persona_provider.dart b/app/lib/pages/persona/persona_provider.dart index 2daee2f1667..37742c23ace 100644 --- a/app/lib/pages/persona/persona_provider.dart +++ b/app/lib/pages/persona/persona_provider.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:flutter/material.dart'; import 'package:omi/backend/http/api/apps.dart'; @@ -41,7 +40,7 @@ class PersonaProvider extends ChangeNotifier { bool hasTwitterConnection = false; ShowSuccessDialogCallback? onShowSuccessDialog; - File? selectedImage; + XFile? selectedImage; String? selectedImageUrl; Future updatePersonaName() async { @@ -176,7 +175,7 @@ class PersonaProvider extends ChangeNotifier { final ImagePicker picker = ImagePicker(); final XFile? image = await picker.pickImage(source: ImageSource.gallery); if (image != null) { - selectedImage = File(image.path); + selectedImage = image; validateForm(); } notifyListeners(); @@ -186,7 +185,7 @@ class PersonaProvider extends ChangeNotifier { final ImagePicker picker = ImagePicker(); final XFile? image = await picker.pickImage(source: ImageSource.gallery); if (image != null) { - selectedImage = File(image.path); + selectedImage = image; validateForm(); // Update diff --git a/app/lib/services/devices/device_connection.dart b/app/lib/services/devices/device_connection.dart index 86dbf27f255..e5b810e6acb 100644 --- a/app/lib/services/devices/device_connection.dart +++ b/app/lib/services/devices/device_connection.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; diff --git a/app/lib/services/devices/omi_connection.dart b/app/lib/services/devices/omi_connection.dart index aa6c5701a4a..2b68671ec2d 100644 --- a/app/lib/services/devices/omi_connection.dart +++ b/app/lib/services/devices/omi_connection.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'dart:math'; import 'package:awesome_notifications/awesome_notifications.dart'; From 3a5d579dc282c7f95a9129f2a5cd1785d46a9a3b Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Sat, 12 Apr 2025 21:49:45 +0530 Subject: [PATCH 13/16] fixes: made file handling web compatible --- app/lib/backend/http/api/conversations.dart | 3 +- .../providers/speech_profile_provider.dart | 7 +- app/lib/services/wals.dart | 33 +++-- app/lib/utils/audio/wav_bytes.dart | 117 ++++++++------- app/lib/utils/omi_file/omi_file.dart | 3 + .../utils/omi_file/omi_file_interface.dart | 31 ++++ app/lib/utils/omi_file/src/omi_file_io.dart | 83 +++++++++++ app/lib/utils/omi_file/src/omi_file_web.dart | 133 ++++++++++++++++++ app/pubspec.yaml | 1 + 9 files changed, 340 insertions(+), 71 deletions(-) create mode 100644 app/lib/utils/omi_file/omi_file.dart create mode 100644 app/lib/utils/omi_file/omi_file_interface.dart create mode 100644 app/lib/utils/omi_file/src/omi_file_io.dart create mode 100644 app/lib/utils/omi_file/src/omi_file_web.dart diff --git a/app/lib/backend/http/api/conversations.dart b/app/lib/backend/http/api/conversations.dart index 0b09965f640..819238f5669 100644 --- a/app/lib/backend/http/api/conversations.dart +++ b/app/lib/backend/http/api/conversations.dart @@ -9,6 +9,7 @@ import 'package:omi/backend/schema/transcript_segment.dart'; import 'package:omi/env/env.dart'; import 'package:http/http.dart' as http; import 'package:instabug_flutter/instabug_flutter.dart'; +import 'package:omi/utils/omi_file/omi_file.dart'; import 'package:path/path.dart'; Future processInProgressConversation() async { @@ -330,7 +331,7 @@ Future> sendStorageToBackend(XFile file, String sdCardD } } -Future syncLocalFiles(List files) async { +Future syncLocalFiles(List files) async { var request = http.MultipartRequest( 'POST', Uri.parse('${Env.apiBaseUrl}v1/sync-local-files'), diff --git a/app/lib/providers/speech_profile_provider.dart b/app/lib/providers/speech_profile_provider.dart index f1a85c02de6..1739276830f 100644 --- a/app/lib/providers/speech_profile_provider.dart +++ b/app/lib/providers/speech_profile_provider.dart @@ -1,8 +1,10 @@ import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:flutter_provider_utilities/flutter_provider_utilities.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:omi/backend/http/api/speech_profile.dart'; import 'package:omi/backend/http/api/users.dart'; import 'package:omi/backend/preferences.dart'; @@ -166,7 +168,10 @@ class SpeechProfileProvider extends ChangeNotifier updateLoadingText('Memorizing your voice...'); var data = await audioStorage.createWavFile(filename: 'speaker_profile.wav'); try { - await uploadProfile(data.item1); + var bytes = await data.item1.readAsBytes(); + XFile uploadFile = XFile.fromData(Uint8List.fromList(bytes)); + + await uploadProfile(uploadFile); } catch (e) {} updateLoadingText('Personalizing your experience...'); diff --git a/app/lib/services/wals.dart b/app/lib/services/wals.dart index 8f31c2e7b93..72556ee897a 100644 --- a/app/lib/services/wals.dart +++ b/app/lib/services/wals.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:collection'; -import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/foundation.dart'; @@ -9,6 +8,7 @@ import 'package:omi/backend/preferences.dart'; import 'package:omi/backend/schema/bt_device/bt_device.dart'; import 'package:omi/backend/schema/conversation.dart'; import 'package:omi/services/services.dart'; +import 'package:omi/utils/omi_file/omi_file.dart'; import 'package:path_provider/path_provider.dart'; const chunkSizeInSeconds = 60; @@ -248,7 +248,7 @@ class SDCardWalSync implements IWalSync { return connection.getBleStorageBytesListener(onStorageBytesReceived: onStorageBytesReceived); } - Future _flushToDisk(List> chunk, int timerStart) async { + Future _flushToDisk(List> chunk, int timerStart) async { final directory = await getApplicationDocumentsDirectory(); String filePath = '${directory.path}/audio_${timerStart}.bin'; List data = []; @@ -263,13 +263,13 @@ class SDCardWalSync implements IWalSync { data.addAll(Uint32List.fromList([frame.length]).buffer.asUint8List()); data.addAll(byteFrame.buffer.asUint8List()); } - final file = File(filePath); + final file = OmiFile(filePath); await file.writeAsBytes(data); return file; } - Future _readStorageBytesToFile(Wal wal, Function(File f, int offset) callback) async { + Future _readStorageBytesToFile(Wal wal, Function(OmiFile f, int offset) callback) async { var deviceId = wal.device; // Move the offset @@ -370,13 +370,13 @@ class SDCardWalSync implements IWalSync { debugPrint("sync wal: ${wal.id} byte offset: ${wal.storageOffset} ts ${wal.timerStart}"); var resp = SyncLocalFilesResponse(newConversationIds: [], updatedConversationIds: []); - List files = []; + List files = []; var limit = 2; // Read with file chunking int lastOffset = 0; - await _readStorageBytesToFile(wal, (File file, int offset) async { + await _readStorageBytesToFile(wal, (OmiFile file, int offset) async { files.add(file); lastOffset = offset; @@ -633,7 +633,7 @@ class LocalWalSync implements IWalSync { data.addAll(Uint32List.fromList([frame.length]).buffer.asUint8List()); data.addAll(byteFrame.buffer.asUint8List()); } - final file = File(filePath); + final file = OmiFile(filePath); await file.writeAsBytes(data); wal.filePath = filePath; wal.storage = WalStorage.disk; @@ -657,8 +657,9 @@ class LocalWalSync implements IWalSync { Future _deleteWal(Wal wal) async { if (wal.filePath != null && wal.filePath!.isNotEmpty) { try { - final file = File(wal.filePath!); - if (file.existsSync()) { + final file = OmiFile(wal.filePath!); + bool exists =await file.exists(); + if (exists) { await file.delete(); } } catch (e) { @@ -713,7 +714,7 @@ class LocalWalSync implements IWalSync { left = 0; } - List files = []; + List files = []; for (var j = left; j <= right; j++) { var wal = wals[j]; debugPrint("sync id ${wal.id} ${wal.timerStart}"); @@ -726,8 +727,9 @@ class LocalWalSync implements IWalSync { debugPrint("sync wal: ${wal.id} file: ${wal.filePath}"); try { - File file = File(wal.filePath!); - if (!file.existsSync()) { + OmiFile file = OmiFile(wal.filePath!); + bool exists = await file.exists(); + if (!exists) { debugPrint("file ${wal.filePath} is not exists"); wal.status = WalStatus.corrupted; continue; @@ -790,14 +792,15 @@ class LocalWalSync implements IWalSync { // Empty resp var resp = SyncLocalFilesResponse(newConversationIds: [], updatedConversationIds: []); - late File walFile; + late OmiFile walFile; if (wal.filePath == null) { debugPrint("file path is not found. wal id ${wal.id}"); wal.status = WalStatus.corrupted; } try { - File file = File(wal.filePath!); - if (!file.existsSync()) { + OmiFile file = OmiFile(wal.filePath!); + bool exists = await file.exists(); + if (!exists) { debugPrint("file ${wal.filePath} is not exists"); wal.status = WalStatus.corrupted; } else { diff --git a/app/lib/utils/audio/wav_bytes.dart b/app/lib/utils/audio/wav_bytes.dart index 410c47c4b91..4930ee8e455 100644 --- a/app/lib/utils/audio/wav_bytes.dart +++ b/app/lib/utils/audio/wav_bytes.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'dart:io'; import 'dart:math'; +import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/material.dart'; @@ -8,6 +8,8 @@ import 'package:omi/backend/schema/bt_device/bt_device.dart'; import 'package:omi/utils/logger.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:intl/intl.dart'; +import 'package:omi/utils/omi_file/omi_file.dart'; +import 'package:omi/utils/platform.dart'; import 'package:opus_dart/opus_dart.dart'; import 'package:path_provider/path_provider.dart'; import 'package:tuple/tuple.dart'; @@ -168,36 +170,36 @@ class WavBytesUtil { // } // } - static Future listFiles(Directory? directory) async { - if (directory == null) return; - final totalBytes = await _getDirectorySize(directory); + // static Future listFiles(Directory? directory) async { + // if (directory == null) return; + // final totalBytes = await _getDirectorySize(directory); - final totalSize = formatBytes(totalBytes); - debugPrint('Total size of $directory: $totalSize'); - } + // final totalSize = formatBytes(totalBytes); + // debugPrint('Total size of $directory: $totalSize'); + // } - static Future _getDirectorySize(Directory dir) async { - int totalBytes = 0; - - try { - if (dir.existsSync()) { - final List entities = dir.listSync(recursive: true, followLinks: false); - for (var entity in entities) { - if (entity is File) { - // debugPrint('File: ${entity.path}'); - totalBytes += await entity.length(); - } else if (entity is Directory) { - // debugPrint('Directory: ${entity.path}'); - totalBytes += await _getDirectorySize(entity); - } - } - } - } catch (e) { - debugPrint("Error calculating directory size: $e"); - } + // static Future _getDirectorySize(Directory dir) async { + // int totalBytes = 0; + + // try { + // if (dir.existsSync()) { + // final List entities = dir.listSync(recursive: true, followLinks: false); + // for (var entity in entities) { + // if (entity is File) { + // // debugPrint('File: ${entity.path}'); + // totalBytes += await entity.length(); + // } else if (entity is Directory) { + // // debugPrint('Directory: ${entity.path}'); + // totalBytes += await _getDirectorySize(entity); + // } + // } + // } + // } catch (e) { + // debugPrint("Error calculating directory size: $e"); + // } - return totalBytes; - } + // return totalBytes; + // } static String formatBytes(int bytes, {int decimals = 2}) { if (bytes <= 0) return "0 B"; @@ -207,25 +209,31 @@ class WavBytesUtil { return "$size ${suffixes[i]}"; } - static Future getDir() => getTemporaryDirectory(); + static Future getDir() async => + PlatformUtil.isWeb ? "/virtual/web/temp" : (await getTemporaryDirectory()).path; /* * FINISHED TESTING LOGIC * */ static clearTempWavFiles() async { - var dirs = [await getTemporaryDirectory(), await getApplicationDocumentsDirectory()]; - for (var directory in dirs) { - var file0 = File('${(await getDir()).path}/temp.wav'); - if (file0.existsSync()) file0.deleteSync(); - - if (directory.existsSync()) { - final List entities = directory.listSync(recursive: false, followLinks: false); - for (var entity in entities) { - if (entity is File && entity.path.endsWith('.wav')) { - debugPrint('Removing file: ${entity.path}'); - if (entity.existsSync()) { - await entity.delete(); + if (PlatformUtil.isWeb) { + var file0 = OmiFile('${await getDir()}/temp.wav'); + if (await file0.exists()) file0.deleteSync(); + } else { + var dirs = [await getTemporaryDirectory(), await getApplicationDocumentsDirectory()]; + for (var directory in dirs) { + var file0 = OmiFile('${await getDir()}/temp.wav'); + if (await file0.exists()) file0.deleteSync(); + + if (directory.existsSync()) { + final List entities = directory.listSync(recursive: false, followLinks: false); + for (var entity in entities) { + if (entity is File && entity.path.endsWith('.wav')) { + debugPrint('Removing file: ${entity.path}'); + if (entity.existsSync()) { + await entity.delete(); + } } } } @@ -240,17 +248,17 @@ class WavBytesUtil { static tempWavExists() async { final directory = await getDir(); - var file0 = File('${directory.path}/temp.wav'); - return file0.existsSync(); + var file0 = OmiFile('$directory/temp.wav'); + return file0.exists(); } static deleteTempWav() async { final directory = await getDir(); - var file0 = File('${directory.path}/temp.wav'); - if (file0.existsSync()) file0.deleteSync(); + var file0 = OmiFile('$directory/temp.wav'); + if (await file0.exists()) file0.deleteSync(); } - Future>>> createWavFile({String? filename, int removeLastNSeconds = 0}) async { + Future>>> createWavFile({String? filename, int removeLastNSeconds = 0}) async { debugPrint('createWavFile $filename'); List> framesCopy; if (removeLastNSeconds > 0) { @@ -263,13 +271,13 @@ class WavBytesUtil { clearAudioBytes(); } debugPrint('about to write to other codec'); - File file = await createWavByCodec(framesCopy, filename: filename); + OmiFile file = await createWavByCodec(framesCopy, filename: filename); return Tuple2(file, framesCopy); } /// OPUS - Future createWavByCodec(List> frames, {String? filename}) async { + Future createWavByCodec(List> frames, {String? filename}) async { Uint8List wavBytes; if (codec == BleAudioCodec.pcm8 || codec == BleAudioCodec.pcm16) { Int16List samples = getPcmSamples(frames); @@ -298,13 +306,13 @@ class WavBytesUtil { return createWav(wavBytes, filename: filename); } - Future createWav(Uint8List wavBytes, {String? filename}) async { + Future createWav(Uint8List wavBytes, {String? filename}) async { final directory = await getDir(); if (filename == null) { final timestamp = DateFormat('yyyyMMdd_HHmmss').format(DateTime.now()); filename = 'recording-$timestamp.wav'; } - final file = File('${directory.path}/$filename'); + final file = OmiFile('$directory/$filename'); await file.writeAsBytes(wavBytes); debugPrint('WAV file created: ${file.path}'); return file; @@ -526,17 +534,17 @@ class StorageBytesUtil extends WavBytesUtil { } @override - Future createWav(Uint8List wavBytes, {String? filename}) async { + Future createWav(Uint8List wavBytes, {String? filename}) async { final directory = await getDir(); String filename2 = 'recording-$fileNum.wav'; - final file = File('${directory.path}/$filename2'); + final file = OmiFile('$directory/$filename2'); await file.writeAsBytes(wavBytes); debugPrint('WAV file created: ${file.path}'); return file; } @override - Future createWavByCodec(List> frames, {String? filename}) async { + Future createWavByCodec(List> frames, {String? filename}) async { Uint8List wavBytes; List decodedSamples = []; @@ -547,5 +555,6 @@ class StorageBytesUtil extends WavBytesUtil { return createWav(wavBytes); } - static Future getDir() => getTemporaryDirectory(); + static Future getDir() async => + PlatformUtil.isWeb ? "/virtual/web/temp" : (await getTemporaryDirectory()).path; } diff --git a/app/lib/utils/omi_file/omi_file.dart b/app/lib/utils/omi_file/omi_file.dart new file mode 100644 index 00000000000..a530fd003f7 --- /dev/null +++ b/app/lib/utils/omi_file/omi_file.dart @@ -0,0 +1,3 @@ + + +export 'src/omi_file_io.dart' if (dart.library.html) 'src/omi_file_web.dart'; \ No newline at end of file diff --git a/app/lib/utils/omi_file/omi_file_interface.dart b/app/lib/utils/omi_file/omi_file_interface.dart new file mode 100644 index 00000000000..9f6a486bbcf --- /dev/null +++ b/app/lib/utils/omi_file/omi_file_interface.dart @@ -0,0 +1,31 @@ + +abstract class IOmiFile { + String get path; + String get name; + int get size; + + Future exists(); + Future create(); + Future delete(); + Future writeAsString(String contents); + Future readAsString(); + Future append(String contents); + Future rename(String newName); + Future lastModified(); + + + Future writeAsBytes(List bytes); + Future> readAsBytes(); + + void createSync(); + void deleteSync(); + void writeAsStringSync(String contents); + String readAsStringSync(); + void appendSync(String contents); + void renameSync(String newName); + DateTime lastModifiedSync(); + + + void writeAsBytesSync(List bytes); + List readAsBytesSync(); +} diff --git a/app/lib/utils/omi_file/src/omi_file_io.dart b/app/lib/utils/omi_file/src/omi_file_io.dart new file mode 100644 index 00000000000..65f917c2517 --- /dev/null +++ b/app/lib/utils/omi_file/src/omi_file_io.dart @@ -0,0 +1,83 @@ +import 'dart:io'; + +import 'package:omi/utils/omi_file/omi_file_interface.dart'; + +class OmiFile implements IOmiFile { + final File _file; + + OmiFile(String path) : _file = File(path); + + @override + Future append(String contents) async => await _file.writeAsString(contents, mode: FileMode.append); + + @override + void appendSync(String contents) => _file.writeAsStringSync(contents, mode: FileMode.append); + + @override + Future create() async => await _file.create(); + + @override + void createSync() => _file.createSync(); + + @override + Future delete() async => await _file.delete(); + + @override + void deleteSync() => _file.deleteSync(); + + @override + Future exists() async=> await _file.exists(); + + @override + Future lastModified() async => await _file.lastModified(); + + @override + DateTime lastModifiedSync() => _file.lastModifiedSync(); + + @override + String get name => _file.uri.pathSegments.last; + + @override + String get path => _file.path; + + @override + Future> readAsBytes() async => await _file.readAsBytes(); + + @override + List readAsBytesSync() => _file.readAsBytesSync(); + + @override + Future readAsString() async => await _file.readAsString(); + + @override + String readAsStringSync() => _file.readAsStringSync(); + + @override + Future rename(String newName) async { + final directory = _file.parent; + final newPath = '${directory.path}/$newName'; + return await _file.rename(newPath); + } + + @override + File renameSync(String newName) { + final directory = _file.parent; + final newPath = '${directory.path}/$newName'; + return _file.renameSync(newPath); + } + + @override + int get size => _file.lengthSync(); + + @override + Future writeAsBytes(List bytes)async => await _file.writeAsBytes(bytes); + + @override + void writeAsBytesSync(List bytes) => _file.writeAsBytesSync(bytes); + + @override + Future writeAsString(String contents)async => await _file.writeAsString(contents); + + @override + void writeAsStringSync(String contents) => _file.writeAsStringSync(contents); +} diff --git a/app/lib/utils/omi_file/src/omi_file_web.dart b/app/lib/utils/omi_file/src/omi_file_web.dart new file mode 100644 index 00000000000..3751a1317ab --- /dev/null +++ b/app/lib/utils/omi_file/src/omi_file_web.dart @@ -0,0 +1,133 @@ +import 'dart:convert'; +import 'package:idb_shim/idb_browser.dart'; +import 'package:omi/utils/omi_file/omi_file_interface.dart'; + +class OmiFile implements IOmiFile { + static const _dbName = 'omi_file_db'; + static const _storeName = 'omi_files'; + + final String _path; + Database? _db; + + OmiFile(this._path); + + Future get _database async { + if (_db != null) return _db!; + final factory = idbFactoryBrowser; + _db = await factory.open(_dbName, version: 1, onUpgradeNeeded: (e) { + e.database.createObjectStore(_storeName); + }); + return _db!; + } + + + + @override + Future create() async { + await writeAsString(''); + return this; + } + + @override + void createSync() => throw UnsupportedError('Sync operations are not supported on the web'); + + @override + Future delete() async { + final db = await _database; + await db.transaction(_storeName, idbModeReadWrite).objectStore(_storeName).delete(_path); + return this; + } + + @override + void deleteSync() => throw UnsupportedError('Sync operations are not supported on the web'); + + @override + Future exists() async { + final db = await _database; + final value = await db.transaction(_storeName, idbModeReadOnly).objectStore(_storeName).getObject(_path); + return value != null; + } + + @override + Future lastModified() async { + throw UnsupportedError('lastModified not supported on web'); + } + + @override + DateTime lastModifiedSync() => throw UnsupportedError('Sync operations are not supported on the web'); + + @override + String get name => _path.split('/').last; + + @override + String get path => _path; + + @override + Future> readAsBytes() async { + final str = await readAsString(); + return utf8.encode(str); + } + + @override + List readAsBytesSync() => throw UnsupportedError('Sync operations are not supported on the web'); + + @override + Future readAsString() async { + final db = await _database; + final data = await db.transaction(_storeName, idbModeReadOnly).objectStore(_storeName).getObject(_path); + return data?.toString() ?? ''; + } + + @override + String readAsStringSync() => throw UnsupportedError('Sync operations are not supported on the web'); + + @override + Future rename(String newName) async { + final contents = await readAsString(); + await delete(); + final newFile = OmiFile(newName); + await newFile.writeAsString(contents); + return newFile; + } + + @override + void renameSync(String newName) => throw UnsupportedError('Sync operations are not supported on the web'); + + @override + int get size => throw UnsupportedError('Use sizeAsync on web'); + + Future get sizeAsync async { + final bytes = await readAsBytes(); + return bytes.length; + } + + @override + Future writeAsBytes(List bytes) async { + await writeAsString(utf8.decode(bytes)); + return this; + } + + @override + void writeAsBytesSync(List bytes) => throw UnsupportedError('Sync operations are not supported on the web'); + + @override + Future writeAsString(String contents) async { + final db = await _database; + await db.transaction(_storeName, idbModeReadWrite).objectStore(_storeName).put(contents, _path); + return this; + } + + @override + void writeAsStringSync(String contents) => throw UnsupportedError('Sync operations are not supported on the web'); + + @override + Future append(String contents) async { + final existing = await readAsString(); + await writeAsString(existing + contents); + return this; + } + + @override + void appendSync(String contents) => throw UnsupportedError('Sync operations are not supported on the web'); + +} diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 3463def78ee..f1755821504 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -101,6 +101,7 @@ dependencies: file_picker: ^10.0.0 photo_view: ^0.15.0 web: ^1.1.1 + idb_shim: ^2.6.5+1 dependency_overrides: From 5c00cdda14d7c56157015f068ad1077c0220025d Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Sun, 13 Apr 2025 18:03:55 +0530 Subject: [PATCH 14/16] fixes: web compatiable mutlipart request --- app/lib/backend/http/api/apps.dart | 46 +++++++++++++++++--- app/lib/backend/http/api/conversations.dart | 20 ++++++++- app/lib/backend/http/api/messages.dart | 31 +++++++++++-- app/lib/backend/http/api/speech_profile.dart | 10 ++++- 4 files changed, 94 insertions(+), 13 deletions(-) diff --git a/app/lib/backend/http/api/apps.dart b/app/lib/backend/http/api/apps.dart index 64c344a1e9f..bc3324a4a8b 100644 --- a/app/lib/backend/http/api/apps.dart +++ b/app/lib/backend/http/api/apps.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:developer'; - +import 'package:http_parser/http_parser.dart'; +import 'package:mime/mime.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:omi/backend/http/shared.dart'; @@ -80,7 +81,15 @@ Future> uploadAppThumbnail(XFile file) async { 'POST', Uri.parse('${Env.apiBaseUrl}v1/app/thumbnails'), ); - request.files.add(await http.MultipartFile.fromPath('file', file.path, filename: basename(file.path))); + String filename = basename(file.path); + request.files.add( + http.MultipartFile.fromBytes( + 'file', + await file.readAsBytes(), + contentType: MediaType.parse(lookupMimeType(filename) ?? 'application/octet-stream'), + filename: filename, + ), + ); request.headers.addAll({'Authorization': await getAuthHeader()}); try { @@ -190,7 +199,15 @@ Future<(bool, String, String?)> submitAppServer(XFile file, Map 'POST', Uri.parse('${Env.apiBaseUrl}v1/apps'), ); - request.files.add(await http.MultipartFile.fromPath('file', file.path, filename: basename(file.path))); + String filename = basename(file.path); + request.files.add( + http.MultipartFile.fromBytes( + 'file', + await file.readAsBytes(), + contentType: MediaType.parse(lookupMimeType(filename) ?? 'application/octet-stream'), + filename: filename, + ), + ); request.headers.addAll({'Authorization': await getAuthHeader()}); request.fields.addAll({'app_data': jsonEncode(appData)}); debugPrint(jsonEncode(appData)); @@ -227,7 +244,8 @@ Future updateAppServer(XFile? file, Map appData) async { Uri.parse('${Env.apiBaseUrl}v1/apps/${appData['id']}'), ); if (file != null) { - request.files.add(await http.MultipartFile.fromPath('file', file.path, filename: basename(file.path))); + var bytes = await file.readAsBytes(); + request.files.add(http.MultipartFile.fromBytes('file', bytes, filename: basename(file.path))); } request.headers.addAll({'Authorization': await getAuthHeader()}); request.fields.addAll({'app_data': jsonEncode(appData)}); @@ -479,7 +497,15 @@ Future createPersonaApp(XFile file, Map personaData) async 'POST', Uri.parse('${Env.apiBaseUrl}v1/personas'), ); - request.files.add(await http.MultipartFile.fromPath('file', file.path, filename: basename(file.path))); + String filename = basename(file.path); + request.files.add( + http.MultipartFile.fromBytes( + 'file', + await file.readAsBytes(), + filename: filename, + contentType: MediaType.parse(lookupMimeType(filename) ?? 'application/octet-stream'), + ), + ); request.headers.addAll({'Authorization': await getAuthHeader()}); request.fields.addAll({'persona_data': jsonEncode(personaData)}); print(jsonEncode(personaData)); @@ -506,7 +532,15 @@ Future updatePersonaApp(XFile? file, Map personaData) asy Uri.parse('${Env.apiBaseUrl}v1/personas/${personaData['id']}'), ); if (file != null) { - request.files.add(await http.MultipartFile.fromPath('file', file.path, filename: basename(file.path))); + String filename = basename(file.path); + request.files.add( + http.MultipartFile.fromBytes( + 'file', + await file.readAsBytes(), + contentType: MediaType.parse(lookupMimeType(filename) ?? 'application/octet-stream'), + filename: filename, + ), + ); } request.headers.addAll({'Authorization': await getAuthHeader()}); request.fields.addAll({'persona_data': jsonEncode(personaData)}); diff --git a/app/lib/backend/http/api/conversations.dart b/app/lib/backend/http/api/conversations.dart index 819238f5669..c2d4f893f55 100644 --- a/app/lib/backend/http/api/conversations.dart +++ b/app/lib/backend/http/api/conversations.dart @@ -1,7 +1,9 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:http_parser/http_parser.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:mime/mime.dart'; import 'package:omi/backend/http/shared.dart'; import 'package:omi/backend/schema/conversation.dart'; import 'package:omi/backend/schema/structured.dart'; @@ -307,7 +309,13 @@ Future> sendStorageToBackend(XFile file, String sdCardD Uri.parse('${Env.apiBaseUrl}sdcard_memory?date_time=$sdCardDateTimeString'), ); request.headers.addAll({'Authorization': await getAuthHeader()}); - request.files.add(await http.MultipartFile.fromPath('file', file.path, filename: basename(file.path))); + String filename = basename(file.path); + request.files.add(http.MultipartFile.fromBytes( + 'file', + await file.readAsBytes(), + contentType: MediaType.parse(lookupMimeType(filename) ?? 'application/octet-stream'), + filename: filename, + )); try { var streamedResponse = await request.send(); var response = await http.Response.fromStream(streamedResponse); @@ -337,7 +345,15 @@ Future syncLocalFiles(List files) async { Uri.parse('${Env.apiBaseUrl}v1/sync-local-files'), ); for (var file in files) { - request.files.add(await http.MultipartFile.fromPath('files', file.path, filename: basename(file.path))); + String filename = basename(file.path); + request.files.add( + http.MultipartFile.fromBytes( + 'files', + await file.readAsBytes(), + contentType: MediaType.parse(lookupMimeType(filename) ?? 'application/octet-stream'), + filename: filename, + ), + ); } request.headers.addAll({'Authorization': await getAuthHeader()}); diff --git a/app/lib/backend/http/api/messages.dart b/app/lib/backend/http/api/messages.dart index 80e11b0d8cb..93f8fc1ab35 100644 --- a/app/lib/backend/http/api/messages.dart +++ b/app/lib/backend/http/api/messages.dart @@ -1,7 +1,9 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:http_parser/http_parser.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:mime/mime.dart'; import 'package:omi/backend/http/shared.dart'; import 'package:omi/backend/schema/message.dart'; import 'package:omi/env/env.dart'; @@ -134,7 +136,7 @@ Stream sendMessageStreamServer(String text, {String? appId, var messageId = "1000"; // Default new message var buffers = []; - + // Process the streamed response await for (var data in response.stream.transform(utf8.decoder)) { var lines = data.split('\n\n'); // Make sure this is the correct delimiter for your server responses @@ -174,6 +176,7 @@ Stream sendMessageStreamServer(String text, {String? appId, yield ServerMessageChunk.failedMessage(); } } + Future getInitialAppMessage(String? appId) { return makeApiCall( url: '${Env.apiBaseUrl}v1/initial-message?plugin_id=$appId', @@ -196,7 +199,13 @@ Stream sendVoiceMessageStreamServer(List files) async Uri.parse('${Env.apiBaseUrl}v2/voice-messages'), ); for (var file in files) { - request.files.add(await http.MultipartFile.fromPath('files', file.path, filename: basename(file.path))); + String filename = basename(file.path); + request.files.add(http.MultipartFile.fromBytes( + 'files', + await file.readAsBytes(), + filename: filename, + contentType: MediaType.parse(lookupMimeType(filename) ?? 'application/octet-stream'), + )); } request.headers.addAll({'Authorization': await getAuthHeader()}); @@ -253,7 +262,15 @@ Future> sendVoiceMessageServer(List files) async { Uri.parse('${Env.apiBaseUrl}v1/voice-messages'), ); for (var file in files) { - request.files.add(await http.MultipartFile.fromPath('files', file.path, filename: basename(file.path))); + String filename = basename(file.path); + request.files.add( + http.MultipartFile.fromBytes( + 'files', + await file.readAsBytes(), + filename: filename, + contentType: MediaType.parse(lookupMimeType(filename) ?? 'application/octet-stream'), + ), + ); } request.headers.addAll({'Authorization': await getAuthHeader()}); @@ -332,7 +349,13 @@ Future transcribeVoiceMessage(XFile audioFile) async { ); request.headers.addAll({'Authorization': await getAuthHeader()}); - request.files.add(await http.MultipartFile.fromPath('files', audioFile.path)); + String filename = basename(audioFile.path); + request.files.add(http.MultipartFile.fromBytes( + 'files', + await audioFile.readAsBytes(), + filename: filename, + contentType: MediaType.parse(lookupMimeType(filename) ?? 'application/octet-stream'), + )); var streamedResponse = await request.send(); var response = await http.Response.fromStream(streamedResponse); diff --git a/app/lib/backend/http/api/speech_profile.dart b/app/lib/backend/http/api/speech_profile.dart index b62a56d68c0..e0e5dcf0282 100644 --- a/app/lib/backend/http/api/speech_profile.dart +++ b/app/lib/backend/http/api/speech_profile.dart @@ -1,7 +1,9 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:http_parser/http_parser.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:mime/mime.dart'; import 'package:omi/backend/http/shared.dart'; import 'package:omi/env/env.dart'; import 'package:http/http.dart' as http; @@ -40,7 +42,13 @@ Future uploadProfile(XFile file) async { 'POST', Uri.parse('${Env.apiBaseUrl}v3/upload-audio'), ); - request.files.add(await http.MultipartFile.fromPath('file', file.path, filename: basename(file.path))); + String filename = basename(file.path); + request.files.add(http.MultipartFile.fromBytes( + 'file', + await file.readAsBytes(), + contentType: MediaType.parse(lookupMimeType(filename) ?? 'application/octet-stream'), + filename: filename, + )); request.headers.addAll({'Authorization': await getAuthHeader()}); try { From 658e85a1b3f6147e9ba1673cba05ec1e34ec9ad3 Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Sun, 13 Apr 2025 18:04:32 +0530 Subject: [PATCH 15/16] refactor: removed unused 'dart:io' and other imports --- app/lib/backend/http/openai.dart | 1 - app/lib/backend/http/shared.dart | 1 - app/lib/pages/apps/add_app.dart | 1 - app/lib/pages/apps/app_detail/app_detail.dart | 1 - app/lib/pages/apps/update_app.dart | 1 - app/lib/pages/chat/widgets/voice_recorder_widget.dart | 3 --- app/lib/pages/conversation_capturing/page.dart | 1 - app/lib/pages/conversation_detail/maps_util.dart | 1 - app/lib/pages/conversation_detail/page.dart | 1 - app/lib/pages/conversation_detail/share.dart | 10 +++++----- app/lib/pages/home/firmware_mixin.dart | 10 +++------- app/lib/pages/home/page.dart | 2 -- .../pages/memories/widgets/delete_confirmation.dart | 1 - app/lib/pages/onboarding/auth.dart | 1 - .../pages/onboarding/find_device/found_devices.dart | 1 - app/lib/pages/onboarding/name/name_widget.dart | 2 -- .../onboarding/permissions/permissions_widget.dart | 1 - app/lib/pages/settings/change_name_widget.dart | 1 - app/lib/pages/settings/developer.dart | 4 ++-- app/lib/pages/settings/people.dart | 1 - app/lib/providers/onboarding_provider.dart | 1 - app/lib/providers/vad.dart | 8 ++++---- app/lib/services/notifications.dart | 1 - app/lib/utils/alerts/app_dialog.dart | 1 - app/lib/utils/analytics/growthbook.dart | 1 - app/lib/utils/device.dart | 1 - app/lib/utils/file.dart | 11 ++++++----- app/lib/utils/other/temp.dart | 1 - app/lib/widgets/confirmation_dialog.dart | 1 - app/lib/widgets/dialog.dart | 1 - app/lib/widgets/sign_in_button.dart | 1 - app/pubspec.yaml | 3 +++ 32 files changed, 23 insertions(+), 53 deletions(-) diff --git a/app/lib/backend/http/openai.dart b/app/lib/backend/http/openai.dart index ea9a3d08179..9bcc9c5696c 100644 --- a/app/lib/backend/http/openai.dart +++ b/app/lib/backend/http/openai.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/material.dart'; diff --git a/app/lib/backend/http/shared.dart b/app/lib/backend/http/shared.dart index bc5ac995c2f..2999ba3dad6 100644 --- a/app/lib/backend/http/shared.dart +++ b/app/lib/backend/http/shared.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:omi/backend/auth.dart'; import 'package:omi/backend/preferences.dart'; import 'package:omi/env/env.dart'; -import 'package:omi/flavors.dart'; import 'package:omi/utils/logger.dart'; import 'package:http/http.dart' as http; import 'package:instabug_flutter/instabug_flutter.dart'; diff --git a/app/lib/pages/apps/add_app.dart b/app/lib/pages/apps/add_app.dart index ad71065ae08..e4835b151fa 100644 --- a/app/lib/pages/apps/add_app.dart +++ b/app/lib/pages/apps/add_app.dart @@ -8,7 +8,6 @@ import 'package:omi/pages/apps/providers/add_app_provider.dart'; import 'package:omi/pages/apps/widgets/app_metadata_widget.dart'; import 'package:omi/pages/apps/widgets/external_trigger_fields_widget.dart'; import 'package:omi/pages/apps/widgets/full_screen_image_viewer.dart'; -import 'package:omi/pages/apps/widgets/api_keys_widget.dart'; import 'package:omi/pages/apps/widgets/notification_scopes_chips_widget.dart'; import 'package:omi/pages/apps/widgets/payment_details_widget.dart'; import 'package:omi/pages/payments/payment_method_provider.dart'; diff --git a/app/lib/pages/apps/app_detail/app_detail.dart b/app/lib/pages/apps/app_detail/app_detail.dart index 9775d2cf556..d0e25752cc1 100644 --- a/app/lib/pages/apps/app_detail/app_detail.dart +++ b/app/lib/pages/apps/app_detail/app_detail.dart @@ -28,7 +28,6 @@ import 'dart:async'; import '../../../backend/schema/app.dart'; import '../widgets/show_app_options_sheet.dart'; -import 'widgets/app_analytics_widget.dart'; import 'widgets/info_card_widget.dart'; import 'package:timeago/timeago.dart' as timeago; diff --git a/app/lib/pages/apps/update_app.dart b/app/lib/pages/apps/update_app.dart index e204d36bf8d..813387a46ce 100644 --- a/app/lib/pages/apps/update_app.dart +++ b/app/lib/pages/apps/update_app.dart @@ -14,7 +14,6 @@ import 'widgets/capabilities_chips_widget.dart'; import 'widgets/external_trigger_fields_widget.dart'; import 'widgets/payment_details_widget.dart'; import 'widgets/prompt_text_field.dart'; -import 'widgets/action_fields_widget.dart'; class UpdateAppPage extends StatefulWidget { final App app; diff --git a/app/lib/pages/chat/widgets/voice_recorder_widget.dart b/app/lib/pages/chat/widgets/voice_recorder_widget.dart index 65794eab90a..02c4898f25b 100644 --- a/app/lib/pages/chat/widgets/voice_recorder_widget.dart +++ b/app/lib/pages/chat/widgets/voice_recorder_widget.dart @@ -3,10 +3,7 @@ import 'dart:math' as Math; import 'dart:typed_data'; import 'package:flutter/material.dart'; -import 'package:omi/backend/http/api/messages.dart'; import 'package:omi/services/services.dart'; -import 'package:omi/utils/alerts/app_snackbar.dart'; -import 'package:omi/utils/enums.dart'; import 'package:omi/utils/file.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:shimmer/shimmer.dart'; diff --git a/app/lib/pages/conversation_capturing/page.dart b/app/lib/pages/conversation_capturing/page.dart index 9d888a9e906..2103f650e21 100644 --- a/app/lib/pages/conversation_capturing/page.dart +++ b/app/lib/pages/conversation_capturing/page.dart @@ -1,4 +1,3 @@ -import 'dart:async'; import 'package:flutter/material.dart'; import 'package:omi/backend/http/api/users.dart'; diff --git a/app/lib/pages/conversation_detail/maps_util.dart b/app/lib/pages/conversation_detail/maps_util.dart index ef91d18b717..f88694cc09c 100644 --- a/app/lib/pages/conversation_detail/maps_util.dart +++ b/app/lib/pages/conversation_detail/maps_util.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:omi/env/env.dart'; import 'package:map_launcher/map_launcher.dart'; diff --git a/app/lib/pages/conversation_detail/page.dart b/app/lib/pages/conversation_detail/page.dart index a23ce1c2028..aeb041fe0a0 100644 --- a/app/lib/pages/conversation_detail/page.dart +++ b/app/lib/pages/conversation_detail/page.dart @@ -10,7 +10,6 @@ import 'package:omi/pages/conversation_detail/widgets.dart'; import 'package:omi/pages/settings/people.dart'; import 'package:omi/providers/connectivity_provider.dart'; import 'package:omi/providers/conversation_provider.dart'; -import 'package:omi/utils/alerts/app_snackbar.dart'; import 'package:omi/utils/analytics/mixpanel.dart'; import 'package:omi/utils/other/temp.dart'; import 'package:omi/widgets/conversation_bottom_bar.dart'; diff --git a/app/lib/pages/conversation_detail/share.dart b/app/lib/pages/conversation_detail/share.dart index d608c6ecc79..d67d01d3670 100644 --- a/app/lib/pages/conversation_detail/share.dart +++ b/app/lib/pages/conversation_detail/share.dart @@ -1,9 +1,9 @@ -import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:omi/backend/http/api/conversations.dart'; import 'package:omi/backend/schema/conversation.dart'; +import 'package:omi/utils/omi_file/omi_file.dart'; import 'package:path_provider/path_provider.dart'; import 'package:pdf/widgets.dart' as pw; import 'package:share_plus/share_plus.dart'; @@ -76,7 +76,7 @@ void _exportPDF(ServerConversation conversation, bool isTranscript) async { } final directory = await getApplicationDocumentsDirectory(); - final file = File('${directory.path}/conversation_export.pdf'); + final file = OmiFile('${directory.path}/conversation_export.pdf'); await file.writeAsBytes(await pdf.save()); await Share.shareXFiles([XFile(file.path)], text: header); @@ -84,7 +84,7 @@ void _exportPDF(ServerConversation conversation, bool isTranscript) async { void _exportTranscriptTxt(ServerConversation conversation) async { final directory = await getApplicationDocumentsDirectory(); - final transcriptFile = File('${directory.path}/conversation_transcript.txt'); + final transcriptFile = OmiFile('${directory.path}/conversation_transcript.txt'); final transcript = "$header${conversation.getTranscript(generate: true)}"; await transcriptFile.writeAsString(transcript); @@ -93,14 +93,14 @@ void _exportTranscriptTxt(ServerConversation conversation) async { void _exportSummaryTxt(ServerConversation conversation) async { final directory = await getApplicationDocumentsDirectory(); - final summaryFile = File('${directory.path}/summary.txt'); + final summaryFile = OmiFile('${directory.path}/summary.txt'); await summaryFile.writeAsString("$header${conversation.structured.toString()}"); await Share.shareXFiles([XFile(summaryFile.path)], text: header); } void _exportSummaryMarkdown(ServerConversation conversation) async { final directory = await getApplicationDocumentsDirectory(); - final markdownFile = File('${directory.path}/conversation_export.md'); + final markdownFile = OmiFile('${directory.path}/conversation_export.md'); final structured = conversation.structured; final markdown = """ # Summary Export diff --git a/app/lib/pages/home/firmware_mixin.dart b/app/lib/pages/home/firmware_mixin.dart index 42d5906a836..7ed2ad38d3f 100644 --- a/app/lib/pages/home/firmware_mixin.dart +++ b/app/lib/pages/home/firmware_mixin.dart @@ -1,18 +1,14 @@ -import 'dart:convert'; -import 'dart:io'; + import 'dart:typed_data'; import 'package:flutter/widgets.dart'; -import 'package:omi/backend/http/shared.dart'; import 'package:omi/backend/schema/bt_device/bt_device.dart'; -import 'package:omi/env/env.dart'; import 'package:omi/http/api/device.dart'; import 'package:omi/services/services.dart'; import 'package:omi/utils/device.dart'; import 'package:nordic_dfu/nordic_dfu.dart'; -import 'package:package_info_plus/package_info_plus.dart'; +import 'package:omi/utils/omi_file/omi_file.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:version/version.dart'; import 'package:http/http.dart' as http; mixin FirmwareMixin on State { @@ -135,7 +131,7 @@ mixin FirmwareMixin on State { debugPrint('downloadPercentage: ${downloaded / r.contentLength! * 100}'); // Need Help: // Save the file - File file = File('$dir/firmware.zip'); + OmiFile file = OmiFile('$dir/firmware.zip'); final Uint8List bytes = Uint8List(r.contentLength!); int offset = 0; for (List chunk in chunks) { diff --git a/app/lib/pages/home/page.dart b/app/lib/pages/home/page.dart index 3e73854a610..3675b244c03 100644 --- a/app/lib/pages/home/page.dart +++ b/app/lib/pages/home/page.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -17,7 +16,6 @@ import 'package:omi/pages/conversations/conversations_page.dart'; import 'package:omi/pages/memories/page.dart'; import 'package:omi/pages/home/widgets/chat_apps_dropdown_widget.dart'; import 'package:omi/pages/persona/persona_profile.dart'; -import 'package:omi/pages/home/widgets/speech_language_sheet.dart'; import 'package:omi/pages/settings/page.dart'; import 'package:omi/providers/app_provider.dart'; import 'package:omi/providers/capture_provider.dart'; diff --git a/app/lib/pages/memories/widgets/delete_confirmation.dart b/app/lib/pages/memories/widgets/delete_confirmation.dart index f8dab314505..a95aab0bbb8 100644 --- a/app/lib/pages/memories/widgets/delete_confirmation.dart +++ b/app/lib/pages/memories/widgets/delete_confirmation.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; diff --git a/app/lib/pages/onboarding/auth.dart b/app/lib/pages/onboarding/auth.dart index 9f280d241d4..cca66593c90 100644 --- a/app/lib/pages/onboarding/auth.dart +++ b/app/lib/pages/onboarding/auth.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; diff --git a/app/lib/pages/onboarding/find_device/found_devices.dart b/app/lib/pages/onboarding/find_device/found_devices.dart index 03c3774e0f6..48a4cf92352 100644 --- a/app/lib/pages/onboarding/find_device/found_devices.dart +++ b/app/lib/pages/onboarding/find_device/found_devices.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_provider_utilities/flutter_provider_utilities.dart'; import 'package:omi/backend/schema/bt_device/bt_device.dart'; import 'package:omi/providers/onboarding_provider.dart'; -import 'package:omi/widgets/dialog.dart'; import 'package:gradient_borders/gradient_borders.dart'; import 'package:provider/provider.dart'; diff --git a/app/lib/pages/onboarding/name/name_widget.dart b/app/lib/pages/onboarding/name/name_widget.dart index 7b2a9d407c9..47849429ef9 100644 --- a/app/lib/pages/onboarding/name/name_widget.dart +++ b/app/lib/pages/onboarding/name/name_widget.dart @@ -4,9 +4,7 @@ import 'package:omi/backend/preferences.dart'; import 'package:gradient_borders/gradient_borders.dart'; import 'package:omi/utils/analytics/intercom_manager.dart'; -import 'package:omi/providers/home_provider.dart'; import 'package:omi/utils/analytics/intercom_manager.dart'; -import 'package:provider/provider.dart'; class NameWidget extends StatefulWidget { final Function goNext; diff --git a/app/lib/pages/onboarding/permissions/permissions_widget.dart b/app/lib/pages/onboarding/permissions/permissions_widget.dart index 703a44cd357..c858da09b15 100644 --- a/app/lib/pages/onboarding/permissions/permissions_widget.dart +++ b/app/lib/pages/onboarding/permissions/permissions_widget.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:flutter/material.dart'; import 'package:omi/providers/onboarding_provider.dart'; diff --git a/app/lib/pages/settings/change_name_widget.dart b/app/lib/pages/settings/change_name_widget.dart index 9a0572ddde3..116a0913ed2 100644 --- a/app/lib/pages/settings/change_name_widget.dart +++ b/app/lib/pages/settings/change_name_widget.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/cupertino.dart'; diff --git a/app/lib/pages/settings/developer.dart b/app/lib/pages/settings/developer.dart index 191c6d0d7aa..362ec7ec513 100644 --- a/app/lib/pages/settings/developer.dart +++ b/app/lib/pages/settings/developer.dart @@ -1,11 +1,11 @@ import 'dart:convert'; -import 'dart:io'; import 'package:flutter/material.dart'; import 'package:omi/backend/http/api/conversations.dart'; import 'package:omi/backend/schema/conversation.dart'; import 'package:omi/providers/developer_mode_provider.dart'; import 'package:omi/utils/analytics/mixpanel.dart'; +import 'package:omi/utils/omi_file/omi_file.dart'; import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; import 'package:share_plus/share_plus.dart'; @@ -168,7 +168,7 @@ class _DeveloperSettingsPageState extends State { await getConversations(limit: 10000, offset: 0); // 10k for now String json = const JsonEncoder.withIndent(" ").convert(memories); final directory = await getApplicationDocumentsDirectory(); - final file = File('${directory.path}/conversations.json'); + final file = OmiFile('${directory.path}/conversations.json'); await file.writeAsString(json); final result = diff --git a/app/lib/pages/settings/people.dart b/app/lib/pages/settings/people.dart index 867aca39140..e9532ea642b 100644 --- a/app/lib/pages/settings/people.dart +++ b/app/lib/pages/settings/people.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:collection/collection.dart'; import 'package:flutter/cupertino.dart'; diff --git a/app/lib/providers/onboarding_provider.dart b/app/lib/providers/onboarding_provider.dart index 24390b1010b..aef672309b4 100644 --- a/app/lib/providers/onboarding_provider.dart +++ b/app/lib/providers/onboarding_provider.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; diff --git a/app/lib/providers/vad.dart b/app/lib/providers/vad.dart index 2a059458391..2ab987c6777 100644 --- a/app/lib/providers/vad.dart +++ b/app/lib/providers/vad.dart @@ -1,10 +1,10 @@ import 'dart:async'; -import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:flutter_silero_vad/flutter_silero_vad.dart'; import 'package:omi/utils/audio/wav_bytes.dart'; +import 'package:omi/utils/omi_file/omi_file.dart'; import 'package:opus_dart/opus_dart.dart'; import 'package:path_provider/path_provider.dart'; @@ -87,7 +87,7 @@ class AudioProcessorService { Timer(const Duration(seconds: 30), () async { print('Timer started'); Uint8List wavBytes = wavBytesUtil.getUInt8ListBytes(validFrames, sampleRate); - final file = File('${(await getApplicationDocumentsDirectory()).path}/output.wav'); + final file = OmiFile('${(await getApplicationDocumentsDirectory()).path}/output.wav'); await file.writeAsBytes(wavBytes); }); } @@ -209,7 +209,7 @@ class AudioProcessorService { ..setUint8(0x27, 0x61) // 'a' ..setUint32(40, pcmBytes.length, Endian.little); // Subchunk2Size - File(filePath).writeAsBytesSync(wavHeader.buffer.asUint8List() + pcmBytes); + OmiFile(filePath).writeAsBytesSync(wavHeader.buffer.asUint8List() + pcmBytes); } /// Transforms a list of bytes into an [Int16List] for processing. @@ -226,6 +226,6 @@ class AudioProcessorService { Future onnxModelToLocal() async { final data = await rootBundle.load('assets/silero_vad.v5.onnx'); final bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - File(await modelPath).writeAsBytesSync(bytes); + OmiFile(await modelPath).writeAsBytesSync(bytes); } } diff --git a/app/lib/services/notifications.dart b/app/lib/services/notifications.dart index 288657b06e2..4ba521bbfe5 100644 --- a/app/lib/services/notifications.dart +++ b/app/lib/services/notifications.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'dart:isolate'; import 'dart:math'; import 'dart:ui'; diff --git a/app/lib/utils/alerts/app_dialog.dart b/app/lib/utils/alerts/app_dialog.dart index d078d424cba..8c852816cb3 100644 --- a/app/lib/utils/alerts/app_dialog.dart +++ b/app/lib/utils/alerts/app_dialog.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; diff --git a/app/lib/utils/analytics/growthbook.dart b/app/lib/utils/analytics/growthbook.dart index e6fd85eb269..f789ac24a97 100644 --- a/app/lib/utils/analytics/growthbook.dart +++ b/app/lib/utils/analytics/growthbook.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:flutter/material.dart'; import 'package:omi/backend/preferences.dart'; diff --git a/app/lib/utils/device.dart b/app/lib/utils/device.dart index d6801509823..e40bba82821 100644 --- a/app/lib/utils/device.dart +++ b/app/lib/utils/device.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:omi/utils/platform.dart'; import 'package:version/version.dart'; import 'package:package_info_plus/package_info_plus.dart'; diff --git a/app/lib/utils/file.dart b/app/lib/utils/file.dart index 45b6e95ae78..3a792f133b8 100644 --- a/app/lib/utils/file.dart +++ b/app/lib/utils/file.dart @@ -1,12 +1,13 @@ -import 'dart:io'; + import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:omi/utils/audio/wav_bytes.dart'; +import 'package:omi/utils/omi_file/omi_file.dart'; import 'package:path_provider/path_provider.dart'; class FileUtils { - static Future saveAudioBytesToTempFile(List> chunk, int timerStart) async { + static Future saveAudioBytesToTempFile(List> chunk, int timerStart) async { final directory = await getTemporaryDirectory(); String filePath = '${directory.path}/audio_${timerStart}.bin'; List data = []; @@ -21,13 +22,13 @@ class FileUtils { data.addAll(Uint32List.fromList([frame.length]).buffer.asUint8List()); data.addAll(byteFrame.buffer.asUint8List()); } - final file = File(filePath); + final file = OmiFile(filePath); await file.writeAsBytes(data); return file; } - static Future convertPcmToWavFile(Uint8List pcmBytes, int sampleRate, int channels) async { + static Future convertPcmToWavFile(Uint8List pcmBytes, int sampleRate, int channels) async { try { // Convert PCM to WAV bytes final wavBytes = WavBytes.fromPcm( @@ -39,7 +40,7 @@ class FileUtils { // Create a temporary file final tempDir = await getTemporaryDirectory(); final tempPath = '${tempDir.path}/recording_${DateTime.now().millisecondsSinceEpoch}.wav'; - final file = File(tempPath); + final file = OmiFile(tempPath); // Write WAV bytes to file await file.writeAsBytes(wavBytes); diff --git a/app/lib/utils/other/temp.dart b/app/lib/utils/other/temp.dart index f6bcbef3ac7..45d00f892fe 100644 --- a/app/lib/utils/other/temp.dart +++ b/app/lib/utils/other/temp.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; diff --git a/app/lib/widgets/confirmation_dialog.dart b/app/lib/widgets/confirmation_dialog.dart index ce3bfec30c7..14d95532378 100644 --- a/app/lib/widgets/confirmation_dialog.dart +++ b/app/lib/widgets/confirmation_dialog.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; diff --git a/app/lib/widgets/dialog.dart b/app/lib/widgets/dialog.dart index 7cb6034958d..2c69202e2f3 100644 --- a/app/lib/widgets/dialog.dart +++ b/app/lib/widgets/dialog.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; diff --git a/app/lib/widgets/sign_in_button.dart b/app/lib/widgets/sign_in_button.dart index a2d2ea6c07a..6881f103548 100644 --- a/app/lib/widgets/sign_in_button.dart +++ b/app/lib/widgets/sign_in_button.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'package:flutter/material.dart'; import 'package:omi/gen/assets.gen.dart'; diff --git a/app/pubspec.yaml b/app/pubspec.yaml index f1755821504..b1257a15a18 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -104,6 +104,9 @@ dependencies: idb_shim: ^2.6.5+1 + vector_graphics: any + http_parser: ^4.1.2 + mime: ^2.0.0 dependency_overrides: http: ^1.2.1 uuid: ^4.4.0 From e31a9fc729522a79b74cff7329618f1173413a2e Mon Sep 17 00:00:00 2001 From: Rutik7066 Date: Wed, 16 Apr 2025 16:32:59 +0530 Subject: [PATCH 16/16] fixes --- app/lib/backend/auth.dart | 3 +- app/lib/backend/http/api/messages.dart | 3 +- .../chat/widgets/voice_recorder_widget.dart | 46 ++++++++-------- app/lib/pages/settings/webview.dart | 54 ++++++++++--------- 4 files changed, 56 insertions(+), 50 deletions(-) diff --git a/app/lib/backend/auth.dart b/app/lib/backend/auth.dart index 287dd970aae..1319c3ad51d 100644 --- a/app/lib/backend/auth.dart +++ b/app/lib/backend/auth.dart @@ -110,8 +110,7 @@ Future signInWithGoogle() async { try { debugPrint('Signing in with Google'); // Trigger the authentication flow - final GoogleSignInAccount? googleUser = await GoogleSignIn(scopes: ['profile', 'email'], - // clientId: "1031333818730-l9meebge7tpc0qmquvq6t5qpoe09o1ra.apps.googleusercontent.com", + final GoogleSignInAccount? googleUser = await GoogleSignIn(scopes: ['profile', 'email'] ).signIn(); debugPrint('Google User: $googleUser'); diff --git a/app/lib/backend/http/api/messages.dart b/app/lib/backend/http/api/messages.dart index 93f8fc1ab35..6b07a6a5dd5 100644 --- a/app/lib/backend/http/api/messages.dart +++ b/app/lib/backend/http/api/messages.dart @@ -8,6 +8,7 @@ import 'package:omi/backend/http/shared.dart'; import 'package:omi/backend/schema/message.dart'; import 'package:omi/env/env.dart'; import 'package:omi/utils/logger.dart'; +import 'package:omi/utils/omi_file/omi_file.dart'; import 'package:omi/utils/other/string_utils.dart'; import 'package:http/http.dart' as http; import 'package:instabug_flutter/instabug_flutter.dart'; @@ -341,7 +342,7 @@ Future reportMessageServer(String messageId) async { } } -Future transcribeVoiceMessage(XFile audioFile) async { +Future transcribeVoiceMessage(OmiFile audioFile) async { try { var request = http.MultipartRequest( 'POST', diff --git a/app/lib/pages/chat/widgets/voice_recorder_widget.dart b/app/lib/pages/chat/widgets/voice_recorder_widget.dart index 02c4898f25b..d1d38372a57 100644 --- a/app/lib/pages/chat/widgets/voice_recorder_widget.dart +++ b/app/lib/pages/chat/widgets/voice_recorder_widget.dart @@ -3,7 +3,9 @@ import 'dart:math' as Math; import 'dart:typed_data'; import 'package:flutter/material.dart'; +import 'package:omi/backend/http/api/messages.dart'; import 'package:omi/services/services.dart'; +import 'package:omi/utils/alerts/app_snackbar.dart'; import 'package:omi/utils/file.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:shimmer/shimmer.dart'; @@ -179,28 +181,28 @@ class _VoiceRecorderWidgetState extends State with SingleTi 1, // Mono channel ); - // try { - // final transcript = await transcribeVoiceMessage(audioFile); - // if (mounted) { - // setState(() { - // _transcript = transcript; - // _state = RecordingState.transcribeSuccess; - // _isProcessing = false; - // }); - // if (transcript.isNotEmpty) { - // widget.onTranscriptReady(transcript); - // } - // } - // } catch (e) { - // debugPrint('Error processing recording: $e'); - // if (mounted) { - // setState(() { - // _state = RecordingState.transcribeFailed; - // _isProcessing = false; - // }); - // } - // AppSnackbar.showSnackbarError('Failed to transcribe audio'); - // } + try { + final transcript = await transcribeVoiceMessage(audioFile); + if (mounted) { + setState(() { + _transcript = transcript; + _state = RecordingState.transcribeSuccess; + _isProcessing = false; + }); + if (transcript.isNotEmpty) { + widget.onTranscriptReady(transcript); + } + } + } catch (e) { + debugPrint('Error processing recording: $e'); + if (mounted) { + setState(() { + _state = RecordingState.transcribeFailed; + _isProcessing = false; + }); + } + AppSnackbar.showSnackbarError('Failed to transcribe audio'); + } } void _retry() { diff --git a/app/lib/pages/settings/webview.dart b/app/lib/pages/settings/webview.dart index 418dc227f7b..c72ff09bf70 100644 --- a/app/lib/pages/settings/webview.dart +++ b/app/lib/pages/settings/webview.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:omi/utils/platform.dart'; import 'package:webview_flutter/webview_flutter.dart'; class PageWebView extends StatefulWidget { @@ -17,31 +18,34 @@ class _PageWebViewState extends State { @override initState() { - webViewController = WebViewController() - ..setJavaScriptMode(JavaScriptMode.unrestricted) - ..setBackgroundColor(const Color(0x00000000)) - ..setNavigationDelegate( - NavigationDelegate( - onProgress: (int p) { - if (mounted) { - setState(() { - progress = p; - }); - } - }, - onPageStarted: (String url) {}, - onPageFinished: (String url) {}, - onHttpError: (HttpResponseError error) {}, - onWebResourceError: (WebResourceError error) {}, - onNavigationRequest: (NavigationRequest request) { - if (request.url != widget.url) { - return NavigationDecision.prevent; - } - return NavigationDecision.navigate; - }, - ), - ) - ..loadRequest(Uri.parse(widget.url)); + debugPrint("Visit this website: ${widget.url}"); + if (PlatformUtil.isNotWeb) { + webViewController = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setBackgroundColor(const Color(0x00000000)) + ..setNavigationDelegate( + NavigationDelegate( + onProgress: (int p) { + if (mounted) { + setState(() { + progress = p; + }); + } + }, + onPageStarted: (String url) {}, + onPageFinished: (String url) {}, + onHttpError: (HttpResponseError error) {}, + onWebResourceError: (WebResourceError error) {}, + onNavigationRequest: (NavigationRequest request) { + if (request.url != widget.url) { + return NavigationDecision.prevent; + } + return NavigationDecision.navigate; + }, + ), + ) + ..loadRequest(Uri.parse(widget.url)); + } super.initState(); }