From 9bd6399cd56ba3c59839cdf8290478b4452ade35 Mon Sep 17 00:00:00 2001 From: Krille Date: Sat, 30 Nov 2024 11:10:33 +0100 Subject: [PATCH] refactor: Update to new receive sharing intent package --- android/app/src/main/AndroidManifest.xml | 27 ++ .../ShareViewController.swift | 347 +----------------- ios/Podfile | 5 + ios/Runner.xcodeproj/project.pbxproj | 42 ++- ios/Runner/Info.plist | 2 +- lib/pages/chat_list/chat_list.dart | 56 +-- .../matrix_file_extension.dart | 2 +- pubspec.lock | 4 +- pubspec.yaml | 2 +- 9 files changed, 119 insertions(+), 368 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 149424e6ab..d5dfef41a4 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -49,10 +49,14 @@ + + + + @@ -61,38 +65,61 @@ android:scheme="https" android:host="matrix.to"/> + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/FluffyChat Share/ShareViewController.swift b/ios/FluffyChat Share/ShareViewController.swift index 96d921432f..4d10e6bd8d 100644 --- a/ios/FluffyChat Share/ShareViewController.swift +++ b/ios/FluffyChat Share/ShareViewController.swift @@ -1,335 +1,14 @@ -import UIKit -import Social -import MobileCoreServices -import Photos - -class ShareViewController: SLComposeServiceViewController { - // TODO: IMPORTANT: This should be your host app bundle identifier - let hostAppBundleIdentifier = "im.fluffychat.app" - let sharedKey = "ShareKey" - var sharedMedia: [SharedMediaFile] = [] - var sharedText: [String] = [] - let imageContentType = kUTTypeImage as String - let videoContentType = kUTTypeMovie as String - let textContentType = kUTTypeText as String - let urlContentType = kUTTypeURL as String - let fileURLType = kUTTypeFileURL as String; - - override func isContentValid() -> Bool { - return true - } - - override func viewDidLoad() { - super.viewDidLoad(); - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments. - if let content = extensionContext!.inputItems[0] as? NSExtensionItem { - if let contents = content.attachments { - for (index, attachment) in (contents).enumerated() { - if attachment.hasItemConformingToTypeIdentifier(imageContentType) { - handleImages(content: content, attachment: attachment, index: index) - } else if attachment.hasItemConformingToTypeIdentifier(textContentType) { - handleText(content: content, attachment: attachment, index: index) - } else if attachment.hasItemConformingToTypeIdentifier(fileURLType) { - handleFiles(content: content, attachment: attachment, index: index) - } else if attachment.hasItemConformingToTypeIdentifier(urlContentType) { - handleUrl(content: content, attachment: attachment, index: index) - } else if attachment.hasItemConformingToTypeIdentifier(videoContentType) { - handleVideos(content: content, attachment: attachment, index: index) - } - } - } - } - } - - override func didSelectPost() { - print("didSelectPost"); - } - - override func configurationItems() -> [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: "group.\(this.hostAppBundleIdentifier)") - 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: "group.\(this.hostAppBundleIdentifier)") - 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: "group.\(this.hostAppBundleIdentifier)")! - .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: "group.\(this.hostAppBundleIdentifier)") - 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: "group.\(this.hostAppBundleIdentifier)")! - .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: "group.\(this.hostAppBundleIdentifier)") - 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: "group.\(this.hostAppBundleIdentifier)")! - .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: "group.\(this.hostAppBundleIdentifier)") - userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey) - userDefaults?.synchronize() - this.redirectToHostApp(type: .file) - } - - } else { - self?.dismissWithError() - } - } - } - - private func dismissWithError() { - print("[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) { - let url = URL(string: "ShareMedia://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) { - print("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: "group.\(hostAppBundleIdentifier)")! - .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() { - print("[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! - } +// If you get no such module 'receive_sharing_intent' error. +// Go to Build Phases of your Runner target and +// move `Embed Foundation Extension` to the top of `Thin Binary`. +import receive_sharing_intent + +class ShareViewController: RSIShareViewController { + + // Use this method to return false if you don't want to redirect to host app automatically. + // Default is true + override func shouldAutoRedirect() -> Bool { + return false + } + } - -extension Array { - subscript (safe index: UInt) -> Element? { - return Int(index) < count ? self[Int(index)] : nil - } -} \ No newline at end of file diff --git a/ios/Podfile b/ios/Podfile index fac4d8176f..4c88a11417 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -32,6 +32,11 @@ target 'Runner' do use_modular_headers! flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + + # From package https://pub.dev/packages/receive_sharing_intent + target 'FluffyChat Share' do + inherit! :search_paths + end end post_install do |installer| diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 9d57b73aad..27e9bef000 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 609046320A2D7D2B0D36583B /* Pods_FluffyChat_Share.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9EB6E6475A19949A37A2634 /* Pods_FluffyChat_Share.framework */; }; 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 */; }; @@ -56,10 +57,12 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 09545B0C8C397F94966EA956 /* Pods-FluffyChat Share.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FluffyChat Share.debug.xcconfig"; path = "Target Support Files/Pods-FluffyChat Share/Pods-FluffyChat Share.debug.xcconfig"; sourceTree = ""; }; 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 = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 50DEFC207B70632D9C56ED78 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 58F7B95D036AD8E67B27588D /* Pods-FluffyChat Share.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FluffyChat Share.profile.xcconfig"; path = "Target Support Files/Pods-FluffyChat Share/Pods-FluffyChat Share.profile.xcconfig"; sourceTree = ""; }; 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 = ""; }; 76737C9A857D5FD6D2634A3F /* 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 = ""; }; @@ -80,7 +83,9 @@ C137635D2AD1446100A8F905 /* notification.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = notification.caf; sourceTree = ""; }; C149567B25C7274F00A16396 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; C149567D25C7276200A16396 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + C9EB6E6475A19949A37A2634 /* Pods_FluffyChat_Share.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FluffyChat_Share.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA246783222E02DD03959891 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + F3778959E67CDA0CDB0D97BC /* Pods-FluffyChat Share.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FluffyChat Share.release.xcconfig"; path = "Target Support Files/Pods-FluffyChat Share/Pods-FluffyChat Share.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -96,6 +101,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 609046320A2D7D2B0D36583B /* Pods_FluffyChat_Share.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -106,6 +112,7 @@ isa = PBXGroup; children = ( 50DEFC207B70632D9C56ED78 /* Pods_Runner.framework */, + C9EB6E6475A19949A37A2634 /* Pods_FluffyChat_Share.framework */, ); name = Frameworks; sourceTree = ""; @@ -177,6 +184,9 @@ 76737C9A857D5FD6D2634A3F /* Pods-Runner.debug.xcconfig */, EA246783222E02DD03959891 /* Pods-Runner.release.xcconfig */, 9DB2F3524376810E74C799A8 /* Pods-Runner.profile.xcconfig */, + 09545B0C8C397F94966EA956 /* Pods-FluffyChat Share.debug.xcconfig */, + F3778959E67CDA0CDB0D97BC /* Pods-FluffyChat Share.release.xcconfig */, + 58F7B95D036AD8E67B27588D /* Pods-FluffyChat Share.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -195,9 +205,9 @@ 97C146EC1CF9000F007C117D /* Resources */, C1005C4D261071B5002F4F32 /* Embed App Extensions */, 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, F9C8EE392B9AB471149C306E /* [CP] Embed Pods Frameworks */, 064CBD7CE0D4CD6850C6880A /* [CP] Copy Pods Resources */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, ); buildRules = ( ); @@ -213,6 +223,7 @@ isa = PBXNativeTarget; buildConfigurationList = C1005C51261071B5002F4F32 /* Build configuration list for PBXNativeTarget "FluffyChat Share" */; buildPhases = ( + 67579C1EA0B5C7B918473158 /* [CP] Check Pods Manifest.lock */, C1005C3E261071B5002F4F32 /* Sources */, C1005C3F261071B5002F4F32 /* Frameworks */, C1005C40261071B5002F4F32 /* Resources */, @@ -320,7 +331,29 @@ ); 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"; + }; + 67579C1EA0B5C7B918473158 /* [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-FluffyChat Share-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; }; 8C9CCA7C5C45651F90C7BFDD /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -357,7 +390,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; F9C8EE392B9AB471149C306E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -693,6 +726,7 @@ }; C1005C4E261071B5002F4F32 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 09545B0C8C397F94966EA956 /* Pods-FluffyChat Share.debug.xcconfig */; buildSettings = { CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -727,6 +761,7 @@ }; C1005C4F261071B5002F4F32 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = F3778959E67CDA0CDB0D97BC /* Pods-FluffyChat Share.release.xcconfig */; buildSettings = { CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -758,6 +793,7 @@ }; C1005C50261071B5002F4F32 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 58F7B95D036AD8E67B27588D /* Pods-FluffyChat Share.profile.xcconfig */; buildSettings = { CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index f53992f999..c94401f483 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -33,7 +33,7 @@ im.fluffychat.app.uris CFBundleURLSchemes - ShareMedia + ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER) im.fluffychat matrix diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 553c979df5..22f1982f7f 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -26,7 +25,6 @@ import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import '../../../utils/account_bundles.dart'; import '../../config/setting_keys.dart'; -import '../../utils/matrix_sdk_extensions/matrix_file_extension.dart'; import '../../utils/url_launcher.dart'; import '../../utils/voip/callkeep_manager.dart'; import '../../widgets/fluffy_chat_app.dart'; @@ -196,20 +194,14 @@ class ChatListController extends State // Share content into this room final shareContent = Matrix.of(context).shareContent; if (shareContent != null) { - final shareFile = shareContent.tryGet('file'); + final shareFile = shareContent.tryGet('file'); if (shareContent.tryGet('msgtype') == 'chat.fluffy.shared_file' && shareFile != null) { await showDialog( context: context, useRootNavigator: false, builder: (c) => SendFileDialog( - files: [ - XFile.fromData( - shareFile.bytes, - name: shareFile.name, - mimeType: shareFile.mimeType, - ), - ], + files: [shareFile], room: room, outerContext: context, ), @@ -432,16 +424,32 @@ class ChatListController extends State ? SelectMode.share : SelectMode.normal; - void _processIncomingSharedFiles(List files) { + void _processIncomingSharedMedia(List files) { if (files.isEmpty) return; - final file = File(files.first.path.replaceFirst('file://', '')); + + if (files.length > 1) { + Logs().w( + 'Received ${files.length} incoming shared media but app can only handle the first one', + ); + } + + // We only handle the first file currently + final sharedMedia = files.first; + + // Handle URIs and Texts, which are also passed in path + if (sharedMedia.type case SharedMediaType.text || SharedMediaType.url) { + return _processIncomingSharedText(sharedMedia.path); + } + + final file = XFile( + sharedMedia.path.replaceFirst('file://', ''), + mimeType: sharedMedia.mimeType, + ); Matrix.of(context).shareContent = { 'msgtype': 'chat.fluffy.shared_file', - 'file': MatrixFile( - bytes: file.readAsBytesSync(), - name: file.path, - ).detectFileType, + 'file': file, + if (sharedMedia.message != null) 'body': sharedMedia.message, }; context.go('/rooms'); } @@ -473,18 +481,14 @@ class ChatListController extends State if (!PlatformInfos.isMobile) return; // For sharing images coming from outside the app while the app is in the memory - _intentFileStreamSubscription = ReceiveSharingIntent.getMediaStream() - .listen(_processIncomingSharedFiles, onError: print); + _intentFileStreamSubscription = ReceiveSharingIntent.instance + .getMediaStream() + .listen(_processIncomingSharedMedia, onError: print); // For sharing images coming from outside the app while the app is closed - ReceiveSharingIntent.getInitialMedia().then(_processIncomingSharedFiles); - - // For sharing or opening urls/text coming from outside the app while the app is in the memory - _intentDataStreamSubscription = ReceiveSharingIntent.getTextStream() - .listen(_processIncomingSharedText, onError: print); - - // For sharing or opening urls/text coming from outside the app while the app is closed - ReceiveSharingIntent.getInitialText().then(_processIncomingSharedText); + ReceiveSharingIntent.instance + .getInitialMedia() + .then(_processIncomingSharedMedia); // For receiving shared Uris _intentUriStreamSubscription = linkStream.listen(_processIncomingUris); diff --git a/lib/utils/matrix_sdk_extensions/matrix_file_extension.dart b/lib/utils/matrix_sdk_extensions/matrix_file_extension.dart index 648a0ca69e..9327632cb2 100644 --- a/lib/utils/matrix_sdk_extensions/matrix_file_extension.dart +++ b/lib/utils/matrix_sdk_extensions/matrix_file_extension.dart @@ -1,9 +1,9 @@ import 'dart:io'; -import 'package:file_selector/file_selector.dart'; import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; +import 'package:file_selector/file_selector.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; import 'package:share_plus/share_plus.dart'; diff --git a/pubspec.lock b/pubspec.lock index 7725343a78..6e60615bf8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1630,10 +1630,10 @@ packages: dependency: "direct main" description: name: receive_sharing_intent - sha256: "912bebb551bce75a14098891fd750305b30d53eba0d61cc70cd9973be9866e8d" + sha256: ec76056e4d258ad708e76d85591d933678625318e411564dcb9059048ca3a593 url: "https://pub.dev" source: hosted - version: "1.4.5" + version: "1.8.1" record: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 9667542a41..8a4f4ba521 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -77,7 +77,7 @@ dependencies: provider: ^6.0.2 punycode: ^1.0.0 qr_code_scanner: ^1.0.1 - receive_sharing_intent: 1.4.5 # Update needs more work + receive_sharing_intent: ^1.8.1 record: ^5.1.2 scroll_to_index: ^3.0.1 share_plus: ^10.0.2