diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 07027d3fc6..77e979593f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -79,12 +79,6 @@ - - - - - - diff --git a/core/lib/data/constants/constant.dart b/core/lib/data/constants/constant.dart index 5540b0ac1c..06b5d45eae 100644 --- a/core/lib/data/constants/constant.dart +++ b/core/lib/data/constants/constant.dart @@ -7,4 +7,6 @@ class Constant { static const octetStreamMimeType = 'application/octet-stream'; static const pdfExtension = '.pdf'; static const imageType = 'image'; + static const textVCardMimeType = 'text/x-vcard'; + static const textPlainMimeType = 'text/plain'; } \ No newline at end of file diff --git a/ios/Podfile b/ios/Podfile index d1c2687646..8148d12783 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -32,6 +32,10 @@ target 'Runner' do use_modular_headers! flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + + target 'TeamMailShareExtension' do + inherit! :search_paths + end end post_install do |installer| diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d1d2b04611..c09b7705c0 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -9,6 +9,7 @@ PODS: - AppAuth/Core - better_open_file (0.0.1): - Flutter + - CocoaAsyncSocket (7.6.5) - connectivity_plus (0.0.1): - Flutter - ReachabilitySwift @@ -163,6 +164,10 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - patrol (0.0.1): + - CocoaAsyncSocket (~> 7.6) + - Flutter + - FlutterMacOS - pdf_render (0.0.1): - Flutter - permission_handler_apple (9.1.1): @@ -173,7 +178,7 @@ PODS: - Flutter - PromisesObjC (2.3.1) - ReachabilitySwift (5.0.0) - - receive_sharing_intent (0.0.1): + - receive_sharing_intent (1.8.1): - Flutter - SDWebImage (5.18.10): - SDWebImage/Core (= 5.18.10) @@ -216,6 +221,7 @@ DEPENDENCIES: - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - patrol (from `.symlinks/plugins/patrol/darwin`) - pdf_render (from `.symlinks/plugins/pdf_render/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`) @@ -229,6 +235,7 @@ DEPENDENCIES: SPEC REPOS: trunk: - AppAuth + - CocoaAsyncSocket - DKImagePickerController - DKPhotoGallery - Firebase @@ -294,6 +301,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" + patrol: + :path: ".symlinks/plugins/patrol/darwin" pdf_render: :path: ".symlinks/plugins/pdf_render/ios" permission_handler_apple: @@ -317,6 +326,7 @@ SPEC CHECKSUMS: app_settings: 017320c6a680cdc94c799949d95b84cb69389ebc AppAuth: 182c5b88630569df5acb672720534756c29b3358 better_open_file: 03cf320415d4d3f46b6e00adc4a567d76c1a399d + CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e contacts_service: 849e1f84281804c8bfbec1b4c3eedcb23c5d3eca device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed @@ -350,13 +360,14 @@ SPEC CHECKSUMS: OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + patrol: 0564cee315ff6c86fb802b3647db05cc2d3d0624 pdf_render: 0b4e1a615aab83ce88b26c57753049424908a755 permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 pointer_interceptor_ios: 9280618c0b2eeb80081a343924aa8ad756c21375 printing: 233e1b73bd1f4a05615548e9b5a324c98588640b PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 - receive_sharing_intent: c0d87310754e74c0f9542947e7cbdf3a0335a3b1 + receive_sharing_intent: 79c848f5b045674ad60b9fea3bafea59962ad2c1 SDWebImage: fc8f2d48bbfd72ef39d70e981bd24a3f3be53fec SDWebImageWebPCoder: 633b813fca24f1de5e076bcd7f720c038b23892b share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 @@ -366,6 +377,6 @@ SPEC CHECKSUMS: url_launcher_ios: 68d46cc9766d0c41dbdc884310529557e3cd7a86 workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6 -PODFILE CHECKSUM: ded4724b53568389542fa0f7d7a64c05ffc03971 +PODFILE CHECKSUM: f2eb8f5a17c320a935d112236893d073c572e17e COCOAPODS: 1.14.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 38f84fc654..fe3b934ce1 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 756BBC4C51FCB44047AB39E4 /* Pods_TeamMailShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 143BD7B2CF181BA69CDE351A /* Pods_TeamMailShareExtension.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -48,7 +49,6 @@ F5BBBF512B2EEC37007930E1 /* NetworkExceptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5BBBF502B2EEC37007930E1 /* NetworkExceptions.swift */; }; F5BBBF532B2EECAA007930E1 /* JmapExceptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5BBBF522B2EECAA007930E1 /* JmapExceptions.swift */; }; F5BBBF552B2EEF3D007930E1 /* BundleExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5BBBF542B2EEF3D007930E1 /* BundleExtension.swift */; }; - F5CFE0302B335098005A90A9 /* TwakeLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5EFC07C2B328B9F00829056 /* TwakeLogger.swift */; }; F5CFE0352B335322005A90A9 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; F5D4EA032B2ABF090090DDFC /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5D4EA022B2ABF090090DDFC /* NotificationService.swift */; }; F5D4EA072B2ABF090090DDFC /* TwakeMailNSE.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = F5D4EA002B2ABF090090DDFC /* TwakeMailNSE.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -118,8 +118,11 @@ /* Begin PBXFileReference section */ 06C73A54FA0478D3C89AB5E5 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 07753111B751BFDB3187FE6F /* Pods-TeamMailShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TeamMailShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-TeamMailShareExtension/Pods-TeamMailShareExtension.debug.xcconfig"; sourceTree = ""; }; + 143BD7B2CF181BA69CDE351A /* Pods_TeamMailShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TeamMailShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 1FB76FA91BBCB2BF7B08705B /* Pods-TeamMailShareExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TeamMailShareExtension.profile.xcconfig"; path = "Target Support Files/Pods-TeamMailShareExtension/Pods-TeamMailShareExtension.profile.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 4D124E74293A67D900BA5186 /* RunnerProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerProfile.entitlements; sourceTree = ""; }; 5810EDDA99BEFEACD742F507 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -135,6 +138,7 @@ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B2EAFF659572E6B9F5AFAAF8 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + BC08294F5AE09BF8CC592AD1 /* Pods-TeamMailShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TeamMailShareExtension.release.xcconfig"; path = "Target Support Files/Pods-TeamMailShareExtension/Pods-TeamMailShareExtension.release.xcconfig"; sourceTree = ""; }; F522E87E2C0EE23400DDA35B /* AuthenticationSSOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationSSOTests.swift; sourceTree = ""; }; F522E8852C0EE8B600DDA35B /* CoreUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreUtils.swift; sourceTree = ""; }; F52F992D27FD6EB900346091 /* TeamMailShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = TeamMailShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -197,6 +201,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 756BBC4C51FCB44047AB39E4 /* Pods_TeamMailShareExtension.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -223,6 +228,7 @@ isa = PBXGroup; children = ( 5810EDDA99BEFEACD742F507 /* Pods_Runner.framework */, + 143BD7B2CF181BA69CDE351A /* Pods_TeamMailShareExtension.framework */, ); name = Frameworks; sourceTree = ""; @@ -482,6 +488,9 @@ B2EAFF659572E6B9F5AFAAF8 /* Pods-Runner.debug.xcconfig */, 631346BB444C71671599207F /* Pods-Runner.release.xcconfig */, 06C73A54FA0478D3C89AB5E5 /* Pods-Runner.profile.xcconfig */, + 07753111B751BFDB3187FE6F /* Pods-TeamMailShareExtension.debug.xcconfig */, + BC08294F5AE09BF8CC592AD1 /* Pods-TeamMailShareExtension.release.xcconfig */, + 1FB76FA91BBCB2BF7B08705B /* Pods-TeamMailShareExtension.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -510,8 +519,6 @@ F5D4EA062B2ABF090090DDFC /* PBXTargetDependency */, ); name = Runner; - packageProductDependencies = ( - ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -520,6 +527,7 @@ isa = PBXNativeTarget; buildConfigurationList = F52F993827FD6EB900346091 /* Build configuration list for PBXNativeTarget "TeamMailShareExtension" */; buildPhases = ( + E791D44F3C4EDE784D610B9E /* [CP] Check Pods Manifest.lock */, F52F992927FD6EB900346091 /* Sources */, F52F992A27FD6EB900346091 /* Frameworks */, F52F992B27FD6EB900346091 /* Resources */, @@ -696,7 +704,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; }; 3D8E7FEF3D91E77326493F6D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -735,6 +743,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; + E791D44F3C4EDE784D610B9E /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-TeamMailShareExtension-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -759,7 +789,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F5CFE0302B335098005A90A9 /* TwakeLogger.swift in Sources */, F52F993027FD6EB900346091 /* ShareViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1120,7 +1149,9 @@ }; F52F993927FD6EB900346091 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 07753111B751BFDB3187FE6F /* Pods-TeamMailShareExtension.debug.xcconfig */; buildSettings = { + APP_GROUP_ID = group.com.linagora.teammail; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_OBJC_WEAK = YES; @@ -1160,7 +1191,9 @@ }; F52F993A27FD6EB900346091 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = BC08294F5AE09BF8CC592AD1 /* Pods-TeamMailShareExtension.release.xcconfig */; buildSettings = { + APP_GROUP_ID = group.com.linagora.teammail; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_OBJC_WEAK = YES; @@ -1197,7 +1230,9 @@ }; F52F993B27FD6EB900346091 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 1FB76FA91BBCB2BF7B08705B /* Pods-TeamMailShareExtension.profile.xcconfig */; buildSettings = { + APP_GROUP_ID = group.com.linagora.teammail; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_OBJC_WEAK = YES; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/TeamMailShareExtension.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/TeamMailShareExtension.xcscheme new file mode 100644 index 0000000000..5758ae89aa --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/TeamMailShareExtension.xcscheme @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/TeamMailShareExtension/Info.plist b/ios/TeamMailShareExtension/Info.plist index 400268a436..adb158d904 100644 --- a/ios/TeamMailShareExtension/Info.plist +++ b/ios/TeamMailShareExtension/Info.plist @@ -2,6 +2,8 @@ + AppGroupId + ${APP_GROUP_ID} CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName diff --git a/ios/TeamMailShareExtension/ShareViewController.swift b/ios/TeamMailShareExtension/ShareViewController.swift index 5ece90a089..0e45452f8b 100644 --- a/ios/TeamMailShareExtension/ShareViewController.swift +++ b/ios/TeamMailShareExtension/ShareViewController.swift @@ -1,354 +1,4 @@ -import UIKit -import Social -import MobileCoreServices -import Photos +import receive_sharing_intent -class ShareViewController: SLComposeServiceViewController { - var hostAppBundleIdentifier = "" - let appGroupId = "group.com.linagora.teammail" - let sharedKey = "ShareKey" - var sharedMedia: [SharedMediaFile] = [] - var sharedText: [String] = [] - let imageContentType = kUTTypeImage as String - let textContentType = kUTTypeText as String - let urlContentType = kUTTypeURL as String - let videoContentType = kUTTypeMovie as String - let fileURLType = kUTTypeFileURL as String - - override func isContentValid() -> Bool { - return true - } - - private func loadIds() { - // loading Share extension App Id - let shareExtensionAppBundleIdentifier = Bundle.main.bundleIdentifier!; - - - // convert ShareExtension id to host app id - // By default it is remove last part of id after last point - // For example: com.test.ShareExtension -> com.test - let lastIndexOfPoint = shareExtensionAppBundleIdentifier.lastIndex(of: "."); - hostAppBundleIdentifier = String(shareExtensionAppBundleIdentifier[.. [Any]! { - // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here. - return [] - } - - private func handleText (content: NSExtensionItem, attachment: NSItemProvider, index: Int) { - attachment.loadItem(forTypeIdentifier: textContentType, options: nil) { [weak self] data, error in - - if error == nil, let item = data as? String, let this = self { - - this.sharedText.append(item) - - // If this is the last item, save imagesData in userDefaults and redirect to host app - if index == (content.attachments?.count)! - 1 { - let userDefaults = UserDefaults(suiteName: this.appGroupId) - userDefaults?.set(this.sharedText, forKey: this.sharedKey) - userDefaults?.synchronize() - this.redirectToHostApp(type: .text) - } - - } else { - self?.dismissWithError() - } - } - } - - private func handleUrl (content: NSExtensionItem, attachment: NSItemProvider, index: Int) { - attachment.loadItem(forTypeIdentifier: urlContentType, options: nil) { [weak self] data, error in - - if error == nil, let item = data as? URL, let this = self { - - this.sharedText.append(item.absoluteString) - - // If this is the last item, save imagesData in userDefaults and redirect to host app - if index == (content.attachments?.count)! - 1 { - let userDefaults = UserDefaults(suiteName: this.appGroupId) - userDefaults?.set(this.sharedText, forKey: this.sharedKey) - userDefaults?.synchronize() - this.redirectToHostApp(type: .text) - } - - } else { - self?.dismissWithError() - } - } - } - - private func handleImages (content: NSExtensionItem, attachment: NSItemProvider, index: Int) { - attachment.loadItem(forTypeIdentifier: imageContentType, options: nil) { [weak self] data, error in - - if error == nil, let url = data as? URL, let this = self { - - // Always copy - let fileName = this.getFileName(from: url, type: .image) - let newPath = FileManager.default - .containerURL(forSecurityApplicationGroupIdentifier: this.appGroupId)! - .appendingPathComponent(fileName) - let copied = this.copyFile(at: url, to: newPath) - if(copied) { - this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .image)) - } - - // If this is the last item, save imagesData in userDefaults and redirect to host app - if index == (content.attachments?.count)! - 1 { - let userDefaults = UserDefaults(suiteName: this.appGroupId) - userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey) - userDefaults?.synchronize() - this.redirectToHostApp(type: .media) - } - - } else { - self?.dismissWithError() - } - } - } - - private func handleVideos (content: NSExtensionItem, attachment: NSItemProvider, index: Int) { - attachment.loadItem(forTypeIdentifier: videoContentType, options: nil) { [weak self] data, error in - - if error == nil, let url = data as? URL, let this = self { - - // Always copy - let fileName = this.getFileName(from: url, type: .video) - let newPath = FileManager.default - .containerURL(forSecurityApplicationGroupIdentifier: this.appGroupId)! - .appendingPathComponent(fileName) - let copied = this.copyFile(at: url, to: newPath) - if(copied) { - guard let sharedFile = this.getSharedMediaFile(forVideo: newPath) else { - return - } - this.sharedMedia.append(sharedFile) - } - - // If this is the last item, save imagesData in userDefaults and redirect to host app - if index == (content.attachments?.count)! - 1 { - let userDefaults = UserDefaults(suiteName: this.appGroupId) - userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey) - userDefaults?.synchronize() - this.redirectToHostApp(type: .media) - } - - } else { - self?.dismissWithError() - } - } - } - - private func handleFiles (content: NSExtensionItem, attachment: NSItemProvider, index: Int) { - attachment.loadItem(forTypeIdentifier: fileURLType, options: nil) { [weak self] data, error in - - if error == nil, let url = data as? URL, let this = self { - - // Always copy - let fileName = this.getFileName(from :url, type: .file) - let newPath = FileManager.default - .containerURL(forSecurityApplicationGroupIdentifier: this.appGroupId)! - .appendingPathComponent(fileName) - let copied = this.copyFile(at: url, to: newPath) - if (copied) { - this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .file)) - } - - if index == (content.attachments?.count)! - 1 { - let userDefaults = UserDefaults(suiteName: this.appGroupId) - userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey) - userDefaults?.synchronize() - this.redirectToHostApp(type: .file) - } - - } else { - self?.dismissWithError() - } - } - } - - - private func dismissWithError() { - TwakeLogger.shared.log(message: "[ERROR] Error loading data!") - let alert = UIAlertController(title: "Error", message: "Error loading data", preferredStyle: .alert) - - let action = UIAlertAction(title: "Error", style: .cancel) { _ in - self.dismiss(animated: true, completion: nil) - } - - alert.addAction(action) - present(alert, animated: true, completion: nil) - extensionContext!.completeRequest(returningItems: [], completionHandler: nil) - } - - private func redirectToHostApp(type: RedirectType) { - // ids may not loaded yet so we need loadIds here too - loadIds(); - let url = URL(string: "ShareMedia-\(hostAppBundleIdentifier)://dataUrl=\(sharedKey)#\(type)") - var responder = self as UIResponder? - let selectorOpenURL = sel_registerName("openURL:") - - while (responder != nil) { - if (responder?.responds(to: selectorOpenURL))! { - let _ = responder?.perform(selectorOpenURL, with: url) - } - responder = responder!.next - } - extensionContext!.completeRequest(returningItems: [], completionHandler: nil) - } - - enum RedirectType { - case media - case text - case file - } - - func getExtension(from url: URL, type: SharedMediaType) -> String { - let parts = url.lastPathComponent.components(separatedBy: ".") - var ex: String? = nil - if (parts.count > 1) { - ex = parts.last - } - - if (ex == nil) { - switch type { - case .image: - ex = "PNG" - case .video: - ex = "MP4" - case .file: - ex = "TXT" - } - } - return ex ?? "Unknown" - } - - func getFileName(from url: URL, type: SharedMediaType) -> String { - var name = url.lastPathComponent - - if (name.isEmpty) { - name = UUID().uuidString + "." + getExtension(from: url, type: type) - } - - return name - } - - func copyFile(at srcURL: URL, to dstURL: URL) -> Bool { - do { - if FileManager.default.fileExists(atPath: dstURL.path) { - try FileManager.default.removeItem(at: dstURL) - } - try FileManager.default.copyItem(at: srcURL, to: dstURL) - } catch (let error) { - TwakeLogger.shared.log(message: "Cannot copy item at \(srcURL) to \(dstURL): \(error)") - return false - } - return true - } - - private func getSharedMediaFile(forVideo: URL) -> SharedMediaFile? { - let asset = AVAsset(url: forVideo) - let duration = (CMTimeGetSeconds(asset.duration) * 1000).rounded() - let thumbnailPath = getThumbnailPath(for: forVideo) - - if FileManager.default.fileExists(atPath: thumbnailPath.path) { - return SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video) - } - - var saved = false - let assetImgGenerate = AVAssetImageGenerator(asset: asset) - assetImgGenerate.appliesPreferredTrackTransform = true - // let scale = UIScreen.main.scale - assetImgGenerate.maximumSize = CGSize(width: 360, height: 360) - do { - let img = try assetImgGenerate.copyCGImage(at: CMTimeMakeWithSeconds(600, preferredTimescale: Int32(1.0)), actualTime: nil) - try UIImage.pngData(UIImage(cgImage: img))()?.write(to: thumbnailPath) - saved = true - } catch { - saved = false - } - - return saved ? SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video) : nil - - } - - private func getThumbnailPath(for url: URL) -> URL { - let fileName = Data(url.lastPathComponent.utf8).base64EncodedString().replacingOccurrences(of: "==", with: "") - let path = FileManager.default - .containerURL(forSecurityApplicationGroupIdentifier: appGroupId)! - .appendingPathComponent("\(fileName).jpg") - return path - } - - class SharedMediaFile: Codable { - var path: String; // can be image, video or url path. It can also be text content - var thumbnail: String?; // video thumbnail - var duration: Double?; // video duration in milliseconds - var type: SharedMediaType; - - - init(path: String, thumbnail: String?, duration: Double?, type: SharedMediaType) { - self.path = path - self.thumbnail = thumbnail - self.duration = duration - self.type = type - } - - // Debug method to print out SharedMediaFile details in the console - func toString() { - TwakeLogger.shared.log(message: "[SharedMediaFile] \n\tpath: \(self.path)\n\tthumbnail: \(self.thumbnail)\n\tduration: \(self.duration)\n\ttype: \(self.type)") - } - } - - enum SharedMediaType: Int, Codable { - case image - case video - case file - } - - func toData(data: [SharedMediaFile]) -> Data { - let encodedData = try? JSONEncoder().encode(data) - return encodedData! - } -} - -extension Array { - subscript (safe index: UInt) -> Element? { - return Int(index) < count ? self[Int(index)] : nil - } +class ShareViewController: RSIShareViewController { } diff --git a/lib/features/composer/presentation/extensions/shared_media_file_extension.dart b/lib/features/composer/presentation/extensions/shared_media_file_extension.dart index 93f6caa37f..bdf157a56f 100644 --- a/lib/features/composer/presentation/extensions/shared_media_file_extension.dart +++ b/lib/features/composer/presentation/extensions/shared_media_file_extension.dart @@ -1,22 +1,16 @@ import 'dart:io'; -import 'package:core/utils/platform_info.dart'; import 'package:model/upload/file_info.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/file_extension.dart'; extension SharedMediaFileExtension on SharedMediaFile { - File toFile() { - if (PlatformInfo.isIOS) { - final pathFile = type == SharedMediaType.FILE - ? path.toString().replaceAll('file:/', '').replaceAll('%20', ' ') - : path.toString().replaceAll('%20', ' '); - return File(pathFile); - } else { - return File(path); - } - } + File toFile() => File(path); - FileInfo toFileInfo({bool? isShared}) => toFile().toFileInfo(isInline: type == SharedMediaType.IMAGE, isShared: isShared); + FileInfo toFileInfo({bool? isShared}) => + toFile().toFileInfo( + isInline: type == SharedMediaType.image, + isShared: isShared, + ); } \ No newline at end of file diff --git a/lib/features/home/presentation/home_controller.dart b/lib/features/home/presentation/home_controller.dart index 4f6758cddd..b08595cab5 100644 --- a/lib/features/home/presentation/home_controller.dart +++ b/lib/features/home/presentation/home_controller.dart @@ -1,12 +1,7 @@ import 'package:core/utils/platform_info.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; -import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; -import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; -import 'package:model/email/email_content.dart'; -import 'package:model/email/email_content_type.dart'; -import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart'; import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart'; import 'package:tmail_ui_user/features/cleanup/domain/model/cleanup_rule.dart'; @@ -45,7 +40,7 @@ class HomeController extends ReloadableController { void onInit() { if (PlatformInfo.isMobile) { _initFlutterDownloader(); - _registerReceivingSharingIntent(); + _registerReceivingFileSharing(); } if (PlatformInfo.isIOS) { _registerNotificationClickOnIOS(); @@ -85,20 +80,8 @@ class HomeController extends ReloadableController { ], eagerError: true).then((_) => getAuthenticatedAccountAction()); } - void _registerReceivingSharingIntent() { - _emailReceiveManager.receivingSharingStream.listen((uri) { - if (uri != null) { - if (GetUtils.isEmail(uri.path)) { - _emailReceiveManager.setPendingEmailAddress(EmailAddress(null, uri.path)); - } else if (uri.scheme == "file") { - _emailReceiveManager.setPendingFileInfo([SharedMediaFile(uri.path, null, null, SharedMediaType.FILE)]); - } else { - _emailReceiveManager.setPendingEmailContent(EmailContent(EmailContentType.textPlain, Uri.decodeComponent(uri.path))); - } - } - }); - - _emailReceiveManager.receivingFileSharingStream.listen(_emailReceiveManager.setPendingFileInfo); + void _registerReceivingFileSharing() { + _emailReceiveManager.registerReceivingFileSharingStreamWhileAppClosed(); } void _registerNotificationClickOnIOS() { diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index 23191f91cf..43b732eea7 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -25,6 +25,7 @@ import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/vacation/vacation_response.dart'; import 'package:model/model.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; +import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:rxdart/transformers.dart'; import 'package:tmail_ui_user/features/base/action/ui_action.dart'; import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart'; @@ -241,11 +242,8 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo ComposerArguments? composerArguments; List? _identities; ScrollController? listSearchFilterScrollController; - - late StreamSubscription _emailAddressStreamSubscription; - late StreamSubscription _emailContentStreamSubscription; - late StreamSubscription _fileReceiveManagerStreamSubscription; - + StreamSubscription? _pendingSharedFileInfoSubscription; + StreamSubscription? _receivingFileSharingStreamSubscription; StreamSubscription? _currentEmailIdInNotificationIOSStreamSubscription; final StreamController> _progressStateController = @@ -288,6 +286,9 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo @override void onInit() { + if (PlatformInfo.isMobile) { + _registerReceivingFileSharingStream(); + } _registerStreamListener(); BackButtonInterceptor.add(_onBackButtonInterceptor, name: AppRoutes.dashboard); WidgetsBinding.instance.addPostFrameCallback((_) async { @@ -301,9 +302,6 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo if (PlatformInfo.isWeb) { listSearchFilterScrollController = ScrollController(); } - _registerPendingEmailAddress(); - _registerPendingEmailContents(); - _registerPendingFileInfo(); if (PlatformInfo.isIOS) { _registerPendingCurrentEmailIdInNotification(); } @@ -454,31 +452,85 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo } } - void _registerPendingEmailAddress() { - _emailAddressStreamSubscription = - _emailReceiveManager.pendingEmailAddressInfo.stream.listen((emailAddress) { - if (emailAddress?.email?.isNotEmpty == true) { - goToComposer(ComposerArguments.fromEmailAddress(emailAddress!)); - } - }); - } + void _registerReceivingFileSharingStream() { + _receivingFileSharingStreamSubscription = _emailReceiveManager + .receivingFileSharingStream + .listen( + _emailReceiveManager.setPendingFileInfo, + onError: (err) { + logError('MailboxDashBoardController::_registerReceivingFileSharingStream::receivingFileSharingStream:Exception = $err'); + }, + ); - void _registerPendingEmailContents() { - _emailContentStreamSubscription = - _emailReceiveManager.pendingEmailContentInfo.stream.listen((emailContent) { - if (emailContent?.content.isNotEmpty == true) { - goToComposer(ComposerArguments.fromContentShared([emailContent!].asHtmlString)); - } - }); + _pendingSharedFileInfoSubscription = _emailReceiveManager + .pendingSharedFileInfo + .listen( + _handleReceivingFileSharing, + onError: (err) { + logError('MailboxDashBoardController::_registerReceivingFileSharingStream::pendingSharedFileInfo:Exception = $err'); + }, + ); } - void _registerPendingFileInfo() { - _fileReceiveManagerStreamSubscription = - _emailReceiveManager.pendingFileInfo.stream.listen((listFile) { - if (listFile.isNotEmpty) { - goToComposer(ComposerArguments.fromFileShared(listFile)); - } - }); + void _handleReceivingFileSharing(List listSharedMediaFile) { + log('MailboxDashBoardController::_handleReceivingFileSharing: LIST_LENGTH = ${listSharedMediaFile.length}'); + if (listSharedMediaFile.isEmpty) return; + + for (var file in listSharedMediaFile) { + log('MailboxDashBoardController::_handleReceivingFileSharing:SharedMediaFile = ${file.toMap()}'); + } + + if (listSharedMediaFile.length == 1) { + final sharedMediaFile = listSharedMediaFile.first; + if (sharedMediaFile.path.trim().isEmpty) return; + + switch (sharedMediaFile.type) { + case SharedMediaType.image: + case SharedMediaType.video: + case SharedMediaType.file: + goToComposer( + ComposerArguments.fromFileShared([sharedMediaFile]), + ); + break; + case SharedMediaType.text: + if (sharedMediaFile.mimeType == Constant.textVCardMimeType) { + goToComposer( + ComposerArguments.fromFileShared([sharedMediaFile]), + ); + } else if (sharedMediaFile.mimeType == Constant.textPlainMimeType) { + goToComposer( + ComposerArguments.fromContentShared(sharedMediaFile.path.trim()), + ); + } + break; + case SharedMediaType.url: + if (sharedMediaFile.path.startsWith(RouteUtils.mailtoPrefix)) { + final navigationRouter = RouteUtils.generateNavigationRouterFromMailtoLink(sharedMediaFile.path); + goToComposer( + ComposerArguments.fromMailtoUri( + listEmailAddress: navigationRouter.listEmailAddress, + subject: navigationRouter.subject, + body: navigationRouter.body, + ), + ); + } + break; + case SharedMediaType.mailto: + if (EmailUtils.isEmailAddressValid(sharedMediaFile.path)) { + goToComposer( + ComposerArguments.fromEmailAddress( + EmailAddress(null, sharedMediaFile.path), + ), + ); + } + break; + } + return; + } + + goToComposer( + ComposerArguments.fromFileShared(listSharedMediaFile), + ); } void _registerPendingCurrentEmailIdInNotification() { @@ -2857,14 +2909,15 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo if (PlatformInfo.isWeb) { listSearchFilterScrollController?.dispose(); } - _emailReceiveManager.closeEmailReceiveManagerStream(); if (PlatformInfo.isIOS) { _iosNotificationManager?.dispose(); _currentEmailIdInNotificationIOSStreamSubscription?.cancel(); } - _emailAddressStreamSubscription.cancel(); - _emailContentStreamSubscription.cancel(); - _fileReceiveManagerStreamSubscription.cancel(); + if (PlatformInfo.isMobile) { + _pendingSharedFileInfoSubscription?.cancel(); + _receivingFileSharingStreamSubscription?.cancel(); + _emailReceiveManager.closeEmailReceiveManagerStream(); + } _progressStateController.close(); _refreshActionEventController.close(); _notificationManager.closeStream(); diff --git a/lib/main/utils/email_receive_manager.dart b/lib/main/utils/email_receive_manager.dart index 2d2c84c6c2..32beb62929 100644 --- a/lib/main/utils/email_receive_manager.dart +++ b/lib/main/utils/email_receive_manager.dart @@ -1,75 +1,38 @@ -import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; -import 'package:model/email/email_content.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:rxdart/rxdart.dart'; class EmailReceiveManager { + BehaviorSubject> _pendingSharedFileInfo = + BehaviorSubject.seeded(List.empty(growable: true)); + BehaviorSubject> get pendingSharedFileInfo => + _pendingSharedFileInfo; - BehaviorSubject _pendingEmailAddressInfo = BehaviorSubject.seeded(null); - BehaviorSubject get pendingEmailAddressInfo => _pendingEmailAddressInfo; + Stream> get receivingFileSharingStream => + ReceiveSharingIntent.instance.getMediaStream(); - BehaviorSubject _pendingEmailContentInfo = BehaviorSubject.seeded(null); - BehaviorSubject get pendingEmailContentInfo => _pendingEmailContentInfo; + void registerReceivingFileSharingStreamWhileAppClosed() { + ReceiveSharingIntent.instance.getInitialMedia().then((value) { + setPendingFileInfo(value); - BehaviorSubject> _pendingFileInfo = BehaviorSubject.seeded(List.empty(growable: true)); - BehaviorSubject> get pendingFileInfo => _pendingFileInfo; - - Stream get receivingSharingStream { - return Rx.merge([ - Stream.fromFuture(ReceiveSharingIntent.getInitialTextAsUri()), - ReceiveSharingIntent.getTextStreamAsUri() - ]); - } - Stream> get receivingFileSharingStream { - return Rx.merge([ - Stream.fromFuture(ReceiveSharingIntent.getInitialMedia()), - ReceiveSharingIntent.getMediaStream() - ]); - } - - void setPendingEmailAddress(EmailAddress emailAddress) async { - _clearPendingEmailAddress(); - _pendingEmailAddressInfo.add(emailAddress); - } - - void setPendingEmailContent(EmailContent emailContent) async { - _clearPendingEmailContent(); - _pendingEmailContentInfo.add(emailContent); - } - - void _clearPendingEmailContent() { - if (_pendingEmailContentInfo.isClosed) { - _pendingEmailContentInfo = BehaviorSubject.seeded(null); - } else { - _pendingEmailContentInfo.add(null); - } - } - - void _clearPendingEmailAddress() { - if(_pendingEmailAddressInfo.isClosed) { - _pendingEmailAddressInfo = BehaviorSubject.seeded(null); - } else { - _pendingEmailAddressInfo.add(null); - } + ReceiveSharingIntent.instance.reset(); + }); } void closeEmailReceiveManagerStream() { - _pendingEmailAddressInfo.close(); - _pendingEmailContentInfo.close(); - _pendingFileInfo.close(); + _pendingSharedFileInfo.close(); } - void setPendingFileInfo(List list) async { + void setPendingFileInfo(List list) { _clearPendingFileInfo(); - _pendingFileInfo.add(list); + _pendingSharedFileInfo.add(list); } void _clearPendingFileInfo() { - if(_pendingFileInfo.isClosed) { - _pendingFileInfo = BehaviorSubject.seeded(List.empty(growable: true)); + if(_pendingSharedFileInfo.isClosed) { + _pendingSharedFileInfo = BehaviorSubject.seeded(List.empty(growable: true)); } else { - _pendingFileInfo.add(List.empty(growable: true)); + _pendingSharedFileInfo.add(List.empty(growable: true)); } } } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index bde3028dab..c1e5c47242 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1701,11 +1701,11 @@ packages: dependency: "direct main" description: path: "." - ref: master - resolved-ref: f23f7fb0fad25ae80a88350ad8654851f1ee682f + ref: main + resolved-ref: b388eb837bd1256a9623a8cd0a62cfc21e9f51cf url: "https://github.com/linagora/receive_sharing_intent.git" source: git - version: "1.4.5" + version: "1.8.1" rich_text_composer: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 0d8c50fb59..439b03fb94 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -75,7 +75,7 @@ dependencies: receive_sharing_intent: git: url: https://github.com/linagora/receive_sharing_intent.git - ref: master + ref: main flutter_appauth_web: git: diff --git a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart index cd0e127d55..a6045ae75a 100644 --- a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart +++ b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart @@ -347,9 +347,7 @@ void main() { setUp(() { getEmailsInMailboxInteractor = MockGetEmailsInMailboxInteractor(); - when(emailReceiveManager.pendingEmailAddressInfo).thenAnswer((_) => BehaviorSubject.seeded(null)); - when(emailReceiveManager.pendingEmailContentInfo).thenAnswer((_) => BehaviorSubject.seeded(null)); - when(emailReceiveManager.pendingFileInfo).thenAnswer((_) => BehaviorSubject.seeded([])); + when(emailReceiveManager.pendingSharedFileInfo).thenAnswer((_) => BehaviorSubject.seeded([])); Get.put(mailboxDashboardController); mailboxDashboardController.onReady(); diff --git a/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart b/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart index 85e4756086..910f68bc9f 100644 --- a/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart +++ b/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart @@ -306,9 +306,7 @@ void main() { Get.put(getAllIdentitiesInteractor); Get.put(removeComposerCacheOnWebInteractor); - when(emailReceiveManager.pendingEmailAddressInfo).thenAnswer((_) => BehaviorSubject.seeded(null)); - when(emailReceiveManager.pendingEmailContentInfo).thenAnswer((_) => BehaviorSubject.seeded(null)); - when(emailReceiveManager.pendingFileInfo).thenAnswer((_) => BehaviorSubject.seeded([])); + when(emailReceiveManager.pendingSharedFileInfo).thenAnswer((_) => BehaviorSubject.seeded([])); searchController = SearchController( quickSearchEmailInteractor,