diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 33793d61b..cc790be8d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -264,18 +264,18 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/sqflite_darwin/darwin" SPEC CHECKSUMS: - app_settings: 58017cd26b604ae98c3e65acbdd8ba173703cc82 + app_settings: 5127ae0678de1dcc19f2293271c51d37c89428b2 AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73 - audio_session: 19e9480dbdd4e5f6c4543826b2e8b0e4ab6145fe - audio_waveforms: cd736909ebc6b6a164eb74701d8c705ce3241e1c - device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d + audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0 + audio_waveforms: a6dde7fe7c0ea05f06ffbdb0f7c1b2b2ba6cedcf + device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896 FBAEMKit: cb719c53575a3be86ea873279f30d6a2c4e15881 FBSDKCoreKit: ecdb980a24633ccb012700299ceb16d0235e14d2 FBSDKCoreKit_Basics: 045101c4a9ef10c845347424d73a29aae02c3e43 FBSDKLoginKit: 69eb59b2f839aba635616df6e422acd0ca88030a Firebase: d80354ed7f6df5f9aca55e9eb47cc4b634735eaf - firebase_auth: 3d848b9b866b201e5c8e0c06d8b2cec272fd8825 - firebase_core: ac395f994af4e28f6a38b59e05a88ca57abeb874 + firebase_auth: 9ebbd83276bf977dea1c74dfc199acf9d2a2f42f + firebase_core: 8d552814f6c01ccde5d88939fced4ec26f2f5510 FirebaseAppCheckInterop: 9664c858489710f682766ef54e2b6741d3b62070 FirebaseAuth: ad59a1a7b161e75f74c39f70179d2482d40e2737 FirebaseAuthInterop: 01a804fb074424fd58b92dd50dd0272277199356 @@ -283,32 +283,32 @@ SPEC CHECKSUMS: FirebaseCoreExtension: 3d3f2017a00d06e09ab4ebe065391b0bb642565e FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_blue_plus_darwin: 3ea4ec9133b377febcc8a70b28cd2d2dc9242bd9 - flutter_facebook_auth: 48be46091bd1a5cc5a66055f1228a9d99a28bfaa - flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e - flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9 - flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 - flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d - fluwx: daa284756ce53442b3d0417ceeda66e981906811 - google_sign_in_ios: 4111e87aa5e24a4404f00ea13479f35e571969cc + flutter_blue_plus_darwin: 09444a26fb6bdef523e55b68fc1c59af5a877ea6 + flutter_facebook_auth: 8a9ad5fbfc25b77e7a53ea8986866950b27edcae + flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1 + flutter_local_notifications: 395056b3175ba4f08480a7c5de30cd36d69827e4 + flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13 + flutter_tts: b88dbc8655d3dc961bc4a796e4e16a4cc1795833 + fluwx: 6bf9c5a3a99ad31b0de137dd92370a0d10a60f4b + google_sign_in_ios: 19297361f2c51d7d8ac0201b866ef1fa5d1f94a8 GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 - image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 - just_audio: a42c63806f16995daf5b219ae1d679deb76e6a79 + image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a + just_audio: 4e391f57b79cad2b0674030a00453ca5ce817eed libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d MicrosoftCognitiveServicesSpeech-iOS: 723eccb3e459bbb530df3d9bfe84a291074b09eb - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 - permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 + permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21 - record_darwin: 3b1a8e7d5c0cbf45ad6165b4d83a6ca643d929c3 + record_darwin: fb1f375f1d9603714f55b8708a903bbb91ffdb0a SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868 SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 - share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f - speech_to_text: 627d3fd2194770b51abb324ba45c2d39398f24a8 - sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d + share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a + speech_to_text: 9dc43a5df3cbc2813f8c7cc9bd0fbf94268ed7ac + sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 Try: 5ef669ae832617b3cee58cb2c6f99fb767a4ff96 WechatOpenSDK-XCFramework: 36fb2bea0754266c17184adf4963d7e6ff98b69f diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index cb604b243..b46ef17e1 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3406320CAE3314A73EF78043 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BBE10AD7954527B6BB12CFB /* Pods_RunnerTests.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 591293932D9524D1004A65BB /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 591293922D9524D1004A65BB /* GoogleService-Info.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -68,6 +69,8 @@ 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 4A9190D0838960F143BDC158 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5912938C2D9520CE004A65BB /* RunnerRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerRelease.entitlements; sourceTree = ""; }; + 591293922D9524D1004A65BB /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 5BBE10AD7954527B6BB12CFB /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -161,6 +164,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 5912938C2D9520CE004A65BB /* RunnerRelease.entitlements */, 0A7A32E22D8DBA6D0047F746 /* VoiceInteractionService.swift */, 0A7A32E32D8DBA6D0047F746 /* VolcanoAIService.swift */, 0ADE296F2D8C6C450032BFCB /* AudioSessionManager.swift */, @@ -171,6 +175,7 @@ 0A5EDC032D8B544100215E26 /* BluetoothMediaButtonHelper.swift */, 0A5EDBFB2D89C4FD00215E26 /* ClassicBluetoothHelper.swift */, 97C146FA1CF9000F007C117D /* Main.storyboard */, + 591293922D9524D1004A65BB /* GoogleService-Info.plist */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, @@ -265,6 +270,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 5912938D2D95215F004A65BB /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -293,6 +301,7 @@ 0ADE29692D8C25660032BFCB /* silence.mp3 in Resources */, 0ADE296A2D8C25660032BFCB /* sample.mp3 in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + 591293932D9524D1004A65BB /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -750,6 +759,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements; CURRENT_PROJECT_VERSION = 23; DEVELOPMENT_TEAM = 42C82SZUVX; ENABLE_BITCODE = NO; @@ -802,6 +812,17 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 5912938D2D95215F004A65BB /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.10.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..7ee26c6b2 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,123 @@ +{ + "originHash" : "c63c63846d9c539229e96de38d6af51417e28c0ee9a0bc48bd0f0f19d923c329", + "pins" : [ + { + "identity" : "abseil-cpp-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/abseil-cpp-binary.git", + "state" : { + "revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5", + "version" : "1.2024072200.0" + } + }, + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk", + "state" : { + "revision" : "2e4a4daac9da51f79c131e76cb47e785dd7708c4", + "version" : "11.10.0" + } + }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "defe1970a9099d47765592ebd7ac5f5e9fdb57c6", + "version" : "11.10.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "617af071af9aa1d6a091d59a202910ac482128f9", + "version" : "10.1.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "53156c7ec267db846e6b64c9f4c4e31ba4cf75eb", + "version" : "8.0.2" + } + }, + { + "identity" : "grpc-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/grpc-binary.git", + "state" : { + "revision" : "cc0001a0cf963aa40501d9c2b181e7fc9fd8ec71", + "version" : "1.69.0" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "4d70340d55d7d07cc2fdf8e8125c4c126c1d5f35", + "version" : "4.4.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", + "version" : "101.0.0" + } + }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", + "version" : "1.22.5" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f", + "version" : "1.29.0" + } + } + ], + "version" : 3 +} diff --git a/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..7ee26c6b2 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,123 @@ +{ + "originHash" : "c63c63846d9c539229e96de38d6af51417e28c0ee9a0bc48bd0f0f19d923c329", + "pins" : [ + { + "identity" : "abseil-cpp-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/abseil-cpp-binary.git", + "state" : { + "revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5", + "version" : "1.2024072200.0" + } + }, + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk", + "state" : { + "revision" : "2e4a4daac9da51f79c131e76cb47e785dd7708c4", + "version" : "11.10.0" + } + }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "defe1970a9099d47765592ebd7ac5f5e9fdb57c6", + "version" : "11.10.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "617af071af9aa1d6a091d59a202910ac482128f9", + "version" : "10.1.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "53156c7ec267db846e6b64c9f4c4e31ba4cf75eb", + "version" : "8.0.2" + } + }, + { + "identity" : "grpc-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/grpc-binary.git", + "state" : { + "revision" : "cc0001a0cf963aa40501d9c2b181e7fc9fd8ec71", + "version" : "1.69.0" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "4d70340d55d7d07cc2fdf8e8125c4c126c1d5f35", + "version" : "4.4.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", + "version" : "101.0.0" + } + }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", + "version" : "1.22.5" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f", + "version" : "1.29.0" + } + } + ], + "version" : 3 +} diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist new file mode 100644 index 000000000..c90ada3a3 --- /dev/null +++ b/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,36 @@ + + + + + CLIENT_ID + 907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278 + ANDROID_CLIENT_ID + 907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com + API_KEY + AIzaSyAvyBQcOXuO_q851vevqZIufsPRTMCe808 + GCM_SENDER_ID + 907754492821 + PLIST_VERSION + 1 + BUNDLE_ID + com.yunqiinnovation.deepsound + PROJECT_ID + deepsound-e0e3d + STORAGE_BUCKET + deepsound-e0e3d.firebasestorage.app + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:907754492821:ios:246794fc44b23174145c9c + + \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 05857d848..b44af52ee 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,6 +2,34 @@ +CFBundleURLTypes + + + CFBundleURLName + com.yunqiinnovation.deepsound + CFBundleURLSchemes + + fb3829022037361855 + com.googleusercontent.apps.907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278 + + + +FacebookAppID +3829022037361855 +FacebookClientToken +193d0a2b1757c41cfa557ea8e8f21c33 +FacebookDisplayName +Deepsound + +LSApplicationQueriesSchemes + + fbapi + fb-messenger-api + fbauth2 + fbshareextension + googlechrome + googlechromes + ITSAppUsesNonExemptEncryption CADisableMinimumFrameDurationOnPhone @@ -12,6 +40,8 @@ DeepSound CFBundleExecutable $(EXECUTABLE_NAME) +NSUserTrackingUsageDescription + This application needs to access your tracking data in order to offer better personalized services and advertisements CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion diff --git a/ios/Runner/RunnerRelease.entitlements b/ios/Runner/RunnerRelease.entitlements new file mode 100644 index 000000000..62a4a38c3 --- /dev/null +++ b/ios/Runner/RunnerRelease.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)com.yunqiinnovation.deepsound + + + diff --git a/lib/data/services/asr_service.dart b/lib/data/services/asr_service.dart index 8e18e3433..02c25283e 100644 --- a/lib/data/services/asr_service.dart +++ b/lib/data/services/asr_service.dart @@ -4,28 +4,28 @@ import 'dart:async'; abstract class AsrService { /// 支持的语言 List get supportedLanguages; - + /// 初始化语音识别服务 Future initialize({ List? supportedLanguages, }); - + /// 执行一次性语音识别 - /// + /// /// 返回识别的最终结果 Future recognizeOnce(); - + /// 开始连续语音识别 - /// + /// /// 返回一个包含识别事件的流 Future> startContinuousRecognition(); - + /// 停止连续语音识别 Future stopContinuousRecognition(); - + /// 检查连续识别是否活跃 bool isContinuousRecognitionActive(); - + /// 释放资源 Future dispose(); } @@ -34,19 +34,19 @@ abstract class AsrService { enum RecognitionEventType { /// 最终识别结果 finalResult, - + /// 中间识别结果(实时反馈) intermediateResult, - + /// 会话开始 sessionStarted, - + /// 会话结束 sessionStopped, - + /// 识别取消 canceled, - + /// 识别错误 error, } @@ -55,23 +55,27 @@ enum RecognitionEventType { class RecognitionEvent { /// 事件类型 final RecognitionEventType type; - + /// 识别文本(仅在 finalResult 和 intermediateResult 类型中有效) final String text; - + /// 检测到的语言 final String detectedLanguage; - + + /// 角色 + final String role; + /// 错误信息(仅在 error 和 canceled 类型中有效) final String error; - + RecognitionEvent({ required this.type, this.text = '', this.detectedLanguage = '', + this.role = '', this.error = '', }); - + /// 创建最终结果事件的快捷构造函数 factory RecognitionEvent.finalResult({ required String text, @@ -83,7 +87,7 @@ class RecognitionEvent { detectedLanguage: detectedLanguage, ); } - + /// 创建错误事件的快捷构造函数 factory RecognitionEvent.error(String errorMessage) { return RecognitionEvent( @@ -91,15 +95,17 @@ class RecognitionEvent { error: errorMessage, ); } - + /// 检查是否为最终结果 bool get isFinalResult => type == RecognitionEventType.finalResult; - + /// 检查是否为错误 - bool get isError => type == RecognitionEventType.error || type == RecognitionEventType.canceled; - + bool get isError => + type == RecognitionEventType.error || + type == RecognitionEventType.canceled; + @override String toString() { return 'RecognitionEvent{type: $type, text: $text, detectedLanguage: $detectedLanguage, error: $error}'; } -} \ No newline at end of file +} diff --git a/lib/data/services/deapsound_ai_service.dart b/lib/data/services/deapsound_ai_service.dart index 6b121af4f..5036923b9 100644 --- a/lib/data/services/deapsound_ai_service.dart +++ b/lib/data/services/deapsound_ai_service.dart @@ -22,14 +22,25 @@ class DeapsoundAIService implements AiService { final client = http.Client(); final url = Uri.parse('https://www.deapsound.com/ai/funcchat'); + List contextsList = []; + String msg = ''; + for (var i = 0; i < messages.length; i++) { + Map item = messages[i]; + if (i < messages.length - 1) { + if (item['role'] == 'user') { + contextsList.add(item['content']); + } + } else { + msg = item['content']; + } + } + final headers = { 'Content-Type': 'application/json', 'Authorization': apiKey, }; - final requestBody = { - "msg": messages.last['content'], - }; + final requestBody = {"contexts": contextsList, "msg": msg}; try { final request = http.Request('POST', url) diff --git a/lib/data/services/speech_impl/xunfei_asr_service.dart b/lib/data/services/speech_impl/xunfei_asr_service.dart new file mode 100644 index 000000000..94d4d72dc --- /dev/null +++ b/lib/data/services/speech_impl/xunfei_asr_service.dart @@ -0,0 +1,270 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:typed_data'; +import 'package:crypto/crypto.dart'; +import 'package:record/record.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; +import '../../../core/utils/logger.dart'; +import 'package:get/get.dart'; +import '../asr_service.dart'; + +/// 讯飞 语音识别服务 +/// +class XunfeiAsrService extends GetxService implements AsrService { + final String baseUrl = 'wss://rtasr.xfyun.cn/v1/ws'; + final int _chunkSize = 1280; // 每次发送的数据大小 + + final String appId = '43d87d22'; + final String apiKey = '83f063755e8d342670002f50a4471d42'; + + AudioRecorder? recorder; + Timer? _timer; + + WebSocketChannel? _channel; + bool isConnected = false; //是否链接 + + bool _isInitialized = false; + + final List _defaultSupportedLanguages = ['zh-CN']; + String _languageStr = 'cn'; + + // 连续识别相关 + bool _isContinuousRecognitionActive = false; + StreamController? _eventStreamController; + + @override + List get supportedLanguages => _defaultSupportedLanguages; + + Uint8List _buff = Uint8List(0); //音频缓存区 + BytesBuilder _originaBytes = BytesBuilder(); //原始音频 + bool isRecording = false; + + @override + Future initialize({ + List? supportedLanguages, + }) async { + try { + final List languages = + supportedLanguages ?? _defaultSupportedLanguages; + + String languagetoStr = languages.first.split('-')[1].toLowerCase(); + _languageStr = languagetoStr; + + _isInitialized = true; + Logger.info('Azure 语音识别服务初始化成功'); + return true; + } catch (e) { + Logger.error('Azure 语音识别服务初始化失败: $e'); + _isInitialized = false; + rethrow; + } + } + + @override + Future recognizeOnce() async { + if (!_isInitialized) { + await initialize(); + } + + try { + throw Exception('无效的识别结果格式'); + } catch (e) { + Logger.error('语音识别失败: $e'); + return RecognitionEvent.error(e.toString()); + } + } + + @override + Future> startContinuousRecognition() async { + if (!_isInitialized) { + await initialize(); + } + + try { + _eventStreamController = StreamController.broadcast(); + + await recorder?.dispose(); + recorder = AudioRecorder(); + Stream? stream = await recorder?.startStream( + const RecordConfig( + sampleRate: 16000, + encoder: AudioEncoder.pcm16bits, + numChannels: 1, + ), + ); + isRecording = true; + stream?.listen((data) { + if (isRecording) { + _buff = _appendToBuffer(data); + _originaBytes.add(data); + } + }); + await _tryConnecting(); + _timer = Timer.periodic(const Duration(milliseconds: 40), (timer) async { + //每40毫秒读取一次数据 + var frame = _getAudioData(); + if (frame.isNotEmpty) { + _channel?.sink.add(frame); + } + }); + + _isContinuousRecognitionActive = true; + return _eventStreamController!.stream; + } catch (e) { + Logger.error('开始连续语音识别失败: $e'); + rethrow; + } + } + + //写入音频数据到缓存区中 + Uint8List _appendToBuffer(Uint8List newData) { + var newBuffer = Uint8List(_buff.length + newData.length); + newBuffer.setAll(0, _buff); + newBuffer.setAll(_buff.length, newData); + return newBuffer; + } + + //读取缓存区中一帧数据 + Uint8List _getAudioData() { + if (_buff.length >= _chunkSize) { + // 从缓冲区中读取1280字节的数据 + var data = _buff.sublist(0, _chunkSize); + // 移除已读取的数据 + _buff = _buff.sublist(_chunkSize); + return data; + } else { + return _buff; + } + } + + //尝试连接 + Future _tryConnecting() async { + try { + final url = _geturl(); + _channel = WebSocketChannel.connect(Uri.parse(url)); + _channel?.stream.timeout(const Duration(seconds: 10)); //设置超时时间 + await _channel?.ready; + isConnected = true; + _channel?.stream.listen( + (message) { + _handle(message); + }, + onError: (error) { + isConnected = false; + Logger.error('连接失败: $error'); + }, + onDone: () { + isConnected = false; + Logger.error('WebSocket 连接已关闭!'); + }, + cancelOnError: true, + ); + } catch (e) { + Logger.error("初始化异常----------------$e"); + } + } + + //处理数据 + void _handle(data) { + try { + final result = jsonDecode(data); + if (result['code'] == "0" && result['data'] != "") { + final data = jsonDecode(result['data']); + String msg = ""; + String role = ''; + + List rtList = data['cn']['st']['rt']; + for (var rt in rtList) { + for (var ws in rt['ws']) { + for (var ws in ws['cw']) { + msg += ws['w']; + if (ws['rl'] != "0") { + role = ws['rl']; + } + } + } + } + Logger.info( + "识别到的内容 type:${data['cn']['st']['type']} msg:$msg rl:$role"); + if (msg.isNotEmpty) { + if (data['cn']['st']['type'] == "0") { + _eventStreamController?.add(RecognitionEvent( + type: RecognitionEventType.finalResult, + text: msg, + role: role, + )); + } else { + _eventStreamController?.add(RecognitionEvent( + type: RecognitionEventType.intermediateResult, + text: msg, + role: role, + )); + } + } + } + } catch (e) { + Logger.error("接受消息异常 $e"); + } + } + + ///获取链接地址 + String _geturl() { + // 生成时间戳 + String ts = (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(); + // 计算 baseString + String baseString = md5.convert(utf8.encode(appId + ts)).toString(); + // 计算 signa + var hmacSha1 = Hmac(sha1, utf8.encode(apiKey)); + var signa = hmacSha1.convert(utf8.encode(baseString)); + String signaBase64 = base64Encode(signa.bytes); + String url = ""; + // 构建 WebSocket URL + url = + '$baseUrl?appid=$appId&ts=$ts&lang=$_languageStr&roleType=2&signa=${Uri.encodeComponent(signaBase64)}'; + return url; + } + + @override + Future stopContinuousRecognition() async { + try { + await recorder?.stop(); + isRecording = false; + _timer?.cancel(); + _buff = Uint8List(0); + _originaBytes = BytesBuilder(); + if (isConnected) { + _channel?.sink.add(utf8.encode('{"end": true}')); //结束 + } + + _isContinuousRecognitionActive = false; + return true; + } catch (e) { + Logger.error('停止连续语音识别失败: $e'); + rethrow; + } + } + + @override + bool isContinuousRecognitionActive() { + return _isContinuousRecognitionActive; + } + + @override + Future dispose() async { + try { + await stopContinuousRecognition(); + recorder?.dispose(); + _channel?.sink.close(); + + _eventStreamController?.close(); + _eventStreamController = null; + + _isInitialized = false; + + Logger.info('Azure 语音识别资源已释放'); + } catch (e) { + Logger.error('释放语音识别资源失败: $e'); + _isInitialized = false; + } + } +} diff --git a/lib/modules/login/controllers/facebookLogin.dart b/lib/modules/login/controllers/facebookLogin.dart index d73cc7f87..61e17a4a8 100644 --- a/lib/modules/login/controllers/facebookLogin.dart +++ b/lib/modules/login/controllers/facebookLogin.dart @@ -1,6 +1,13 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; + import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_facebook_auth/flutter_facebook_auth.dart'; import 'package:get/get.dart'; +import 'package:crypto/crypto.dart'; class Facebook { String token = ''; @@ -9,35 +16,90 @@ class Facebook { String email = ''; String pictureurl = ''; String id = ''; + String generateNonce([int length = 32]) { + const charset = + '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._'; + final random = Random.secure(); + return List.generate(length, (_) => charset[random.nextInt(charset.length)]) + .join(); + } + + /// Returns the sha256 hash of [input] in hex notation. + String sha256ofString(String input) { + final bytes = utf8.encode(input); + final digest = sha256.convert(bytes); + return digest.toString(); + } // FaceBook登录 // 修改后的 signInWithFacebook Future signInWithFacebook() async { try { - print("开始 Facebook 登录"); - final LoginResult result = await FacebookAuth.instance.login(); - print("Facebook 登录结果:$result"); - - if (result.status == LoginStatus.success) { - token = result.accessToken!.tokenString; - print("Facebook Token: $token"); - - final credential = FacebookAuthProvider.credential(token); - print("Facebook Token: $credential"); - final userData = await FacebookAuth.instance.getUserData(); - name = userData["name"].toString(); - - email = userData["email"].toString(); - pictureurl = userData["picture"]["data"]["url"].toString(); - id = userData["id"].toString(); - print("user login facebook ${userData..values}"); - print("user login facebook ${email}${pictureurl}${name}${id}"); - - return true; + final rawNonce = generateNonce(); + final nonce = sha256ofString(rawNonce); + final LoginResult loginResult = await FacebookAuth.instance + .login( + loginTracking: LoginTracking.enabled, + nonce: nonce, + ) + .catchError((onError) { + if (kDebugMode) { + //print(onError); + } + throw Exception(onError.message); + }); + if (loginResult.accessToken == null) { + throw Exception(loginResult.message); + } + // Create a credential from the access token + OAuthCredential facebookAuthCredential; + + print("tokenType${loginResult.accessToken!.type}"); + + if (Platform.isIOS) { + // Create a credential from the access token + switch (loginResult.accessToken!.type) { + case AccessTokenType.classic: + final classicToken = loginResult.accessToken as ClassicToken; + token = classicToken.tokenString; + print('Token 字符串: ${classicToken.tokenString}'); + print('classicToken值: ${classicToken}'); + facebookAuthCredential = FacebookAuthProvider.credential( + classicToken.authenticationToken!, + ); + break; + case AccessTokenType.limited: + final limitedToken = loginResult.accessToken as LimitedToken; + token = limitedToken.tokenString; + print('Token 字符串: ${limitedToken.tokenString}'); + print('LimitedToken值: ${limitedToken}'); + facebookAuthCredential = OAuthCredential( + providerId: 'facebook.com', + signInMethod: 'oauth', + idToken: limitedToken.tokenString, + rawNonce: rawNonce, + ); + break; + } } else { - print("登录失败,状态:${result.status},消息:${result.message}"); - return false; + facebookAuthCredential = FacebookAuthProvider.credential( + loginResult.accessToken!.tokenString, + ); + token = loginResult.accessToken!.tokenString; + } + print('facebookAuthCredential值: ${facebookAuthCredential}'); + if (facebookAuthCredential == null) { + throw Exception("Facebook Login Fail."); } + final userData = await FacebookAuth.instance.getUserData(); + name = userData["name"].toString(); + + email = userData["email"].toString(); + pictureurl = userData["picture"]["data"]["url"].toString(); + id = userData["id"].toString(); + print("user login facebook ${email}${pictureurl}${name}${id}"); + + return true; } catch (e) { print("Facebook 登录异常:$e"); return false; diff --git a/lib/modules/login/controllers/googleLogin.dart b/lib/modules/login/controllers/googleLogin.dart index 196699b15..548cbd792 100644 --- a/lib/modules/login/controllers/googleLogin.dart +++ b/lib/modules/login/controllers/googleLogin.dart @@ -33,6 +33,8 @@ class Google { return true; } else { UserCredential userCredential = await loginGoogle(); + token = await userCredential.user?.getIdToken(); + print('Future响应数据tokenResult: $token'); if (token != "") { return true; } else { @@ -61,10 +63,7 @@ class Google { // Obtain the auth details from the request final GoogleSignInAuthentication? googleAuth = await googleUser?.authentication; - accessToken = googleAuth?.accessToken; - token = googleAuth?.idToken; - print('loginGoogle:googleAuth?.idToken= ${token}'); - print('loginGoogle:googleAuth?.accessToken= ${accessToken}'); + // Create a new credential, final credential = GoogleAuthProvider.credential( accessToken: googleAuth?.accessToken, diff --git a/lib/modules/meeting/controllers/meeting_edit_controller.dart b/lib/modules/meeting/controllers/meeting_edit_controller.dart index d24308441..87af7ec73 100644 --- a/lib/modules/meeting/controllers/meeting_edit_controller.dart +++ b/lib/modules/meeting/controllers/meeting_edit_controller.dart @@ -62,13 +62,13 @@ class MeetingEditController extends GetxController void confirm() async { String content = contentController.text; String firstContent = - content.length >= 20 ? content.substring(0, 20) : content; + content.length >= 30 ? content.substring(0, 30) : content; String title = titleController.text.isNotEmpty ? titleController.text : meetingDetailsController.meetingData.value.title; Map data = { 'title': title, - 'firstContent': firstContent, + 'firstContent': firstContent.replaceAll("\n", ""), 'seconds': meetingDetailsController.seconds, 'content': contentController.text, }; @@ -81,7 +81,7 @@ class MeetingEditController extends GetxController final dataMap = Map.from(meetingController.dataList[index]); dataMap['title'] = title; - dataMap['content'] = firstContent; + dataMap['content'] = firstContent.replaceAll("\n", ""); dataMap['seconds'] = meetingDetailsController.seconds; meetingController.dataList[index] = dataMap; } diff --git a/lib/modules/meeting/controllers/meeting_record_controller.dart b/lib/modules/meeting/controllers/meeting_record_controller.dart index 1e569bfd9..dfd364675 100644 --- a/lib/modules/meeting/controllers/meeting_record_controller.dart +++ b/lib/modules/meeting/controllers/meeting_record_controller.dart @@ -5,6 +5,7 @@ import 'package:get/get.dart'; import '../../../data/services/asr_service.dart'; import '../../../data/services/db/sqflite_api.dart'; +import '../../../data/services/speech_impl/xunfei_asr_service.dart'; import 'meeting_controller.dart'; class MeetingRecordController extends GetxController @@ -31,7 +32,8 @@ class MeetingRecordController extends GetxController void onInit() { super.onInit(); lottieController = AnimationController(vsync: this); - _asrService = Get.find(); + Get.put(XunfeiAsrService()); + _asrService = Get.find(); final Map args = Get.arguments ?? {}; title = args['title']; creationTime = DateTime.now().millisecondsSinceEpoch; @@ -41,7 +43,7 @@ class MeetingRecordController extends GetxController void onClose() { lottieController.dispose(); timer?.cancel(); - _asrService.stopContinuousRecognition(); + _asrService.dispose(); _recognitionSubscription?.cancel(); super.onClose(); } @@ -82,7 +84,6 @@ class MeetingRecordController extends GetxController //结束录音 Future _stopRecorder() async { await _asrService.stopContinuousRecognition(); - _recognitionSubscription?.cancel(); } // 处理语音识别事件 @@ -92,7 +93,7 @@ class MeetingRecordController extends GetxController intermediateContent.value = event.text; break; case RecognitionEventType.finalResult: - finalContent.value += event.text; + finalContent.value += _spliceText(event); intermediateContent.value = ''; break; default: @@ -101,14 +102,49 @@ class MeetingRecordController extends GetxController } } + String _spliceText(RecognitionEvent event) { + String eventText = event.text; + String text = ''; + if (finalContent.value.isNotEmpty) { + if (event.role != '') { + final punctuations = { + ',', + '。', + '!', + '?', + ';', + ':', + ',', + '.', + ':', + ';', + '!', + '?' + }; + final firstChar = eventText[0]; + if (punctuations.contains(firstChar)) { + text += '$firstChar\n'; + eventText = eventText.substring(1); + } else { + text += '\n'; + } + text += '说话人${event.role}:'; + } + } else { + text += '说话人${event.role}:'; + } + text += eventText; + return text; + } + // 完成 void complete() async { - String firstContent = finalContent.value.length >= 20 - ? finalContent.value.substring(0, 20) + String firstContent = finalContent.value.length >= 30 + ? finalContent.value.substring(0, 30) : finalContent.value; Map data = { 'title': title, - 'firstContent': firstContent, + 'firstContent': firstContent.replaceAll("\n", ""), 'seconds': seconds.value, 'creationTime': creationTime, 'audio': [], diff --git a/lib/modules/meeting/views/meeting_edit_view.dart b/lib/modules/meeting/views/meeting_edit_view.dart index b85e95c7f..73de47a76 100644 --- a/lib/modules/meeting/views/meeting_edit_view.dart +++ b/lib/modules/meeting/views/meeting_edit_view.dart @@ -237,14 +237,14 @@ class MeetingEditView extends GetView { controller: controller.contentController, style: TextStyle( color: isDarkMode ? Colors.white : Colors.black87, - fontSize: 16.sp, + fontSize: 14.sp, ), maxLines: 50, decoration: InputDecoration( hintText: '编辑记录内容', hintStyle: TextStyle( color: isDarkMode ? Colors.white70 : Colors.black54, - fontSize: 16.sp, + fontSize: 14.sp, ), fillColor: Colors.transparent, border: InputBorder.none, diff --git a/lib/modules/settings/controllers/settings_controller.dart b/lib/modules/settings/controllers/settings_controller.dart index a1f1890b1..9fe723ee2 100644 --- a/lib/modules/settings/controllers/settings_controller.dart +++ b/lib/modules/settings/controllers/settings_controller.dart @@ -8,6 +8,8 @@ import '../../../data/services/speech_factory.dart'; import '../models/language_model.dart'; //import '../../../data/models/user_model.dart'; import '../../../data/models/user_Info.dart'; +import '../../../modules/login/controllers/googleLogin.dart'; +import '../../../modules/login/controllers/facebookLogin.dart'; class SettingsController extends GetxController { // 语言设置 @@ -222,9 +224,15 @@ class SettingsController extends GetxController { targetLanguage.value = temp; } - void signOut() { + Future signOut() async { // 在此实现退出登录逻辑 _storage.write("logintoken", ""); + _storage.write("user_info", ""); + final google = Google(); + final facebook = Facebook(); + + await google.signOutGoogle(); + await facebook.signOutFacebook(); Get.offAllNamed('/login'); } } diff --git a/lib/modules/splash/controllers/splash_controller.dart b/lib/modules/splash/controllers/splash_controller.dart index c52535c3d..b5dd9d823 100644 --- a/lib/modules/splash/controllers/splash_controller.dart +++ b/lib/modules/splash/controllers/splash_controller.dart @@ -76,10 +76,13 @@ class SplashController extends GetxController { // 获取登录Token并跳转 Future _navigateToLogin() async { - final isLoggedIn = await getLoginToken(); + bool isLoggedIn = false; + final token = _storage.read('logintoken'); + print('getLoginToken:进来了${token}'); + if (token != null && token.isNotEmpty) { + isLoggedIn = await getLoginToken(); + } if (isLoggedIn) { - // Get.offAllNamed(Routes.login); // 跳转到登录页 - // Get.offAllNamed(Routes.home); // 跳转到主页 Get.offAllNamed(Routes.pairing); } else { Get.offAllNamed(Routes.login); // 跳转到登录页