From ea31991d503064fe301a34999e163dc67afe650e Mon Sep 17 00:00:00 2001 From: Christian Pauly Date: Sun, 28 Mar 2021 10:43:16 +0200 Subject: [PATCH] fix: Share on iOS --- .../Base.lproj/MainInterface.storyboard | 24 ++ .../FluffyChat Share.entitlements | 10 + ios/FluffyChat Share/Info.plist | 52 +++ .../ShareViewController.swift | 335 ++++++++++++++++++ ios/Runner.xcodeproj/project.pbxproj | 214 +++++++++++ ios/Runner/Info.plist | 25 +- ios/Runner/Runner.entitlements | 8 + lib/views/homeserver_picker.dart | 4 +- 8 files changed, 659 insertions(+), 13 deletions(-) create mode 100644 ios/FluffyChat Share/Base.lproj/MainInterface.storyboard create mode 100644 ios/FluffyChat Share/FluffyChat Share.entitlements create mode 100644 ios/FluffyChat Share/Info.plist create mode 100644 ios/FluffyChat Share/ShareViewController.swift diff --git a/ios/FluffyChat Share/Base.lproj/MainInterface.storyboard b/ios/FluffyChat Share/Base.lproj/MainInterface.storyboard new file mode 100644 index 00000000..286a5089 --- /dev/null +++ b/ios/FluffyChat Share/Base.lproj/MainInterface.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/FluffyChat Share/FluffyChat Share.entitlements b/ios/FluffyChat Share/FluffyChat Share.entitlements new file mode 100644 index 00000000..b6eb37e5 --- /dev/null +++ b/ios/FluffyChat Share/FluffyChat Share.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.famedly.app + + + diff --git a/ios/FluffyChat Share/Info.plist b/ios/FluffyChat Share/Info.plist new file mode 100644 index 00000000..d3a136ec --- /dev/null +++ b/ios/FluffyChat Share/Info.plist @@ -0,0 +1,52 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + FluffyChat Share + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSExtension + + NSExtensionAttributes + + NSExtensionActivationRule + + NSExtensionActivationSupportsFileWithMaxCount + 1 + NSExtensionActivationSupportsImageWithMaxCount + 1 + NSExtensionActivationSupportsMovieWithMaxCount + 1 + NSExtensionActivationSupportsText + + NSExtensionActivationSupportsWebURLWithMaxCount + 1 + + PHSupportedMediaTypes + + Video + Image + + + NSExtensionMainStoryboard + MainInterface + NSExtensionPointIdentifier + com.apple.share-services + + + diff --git a/ios/FluffyChat Share/ShareViewController.swift b/ios/FluffyChat Share/ShareViewController.swift new file mode 100644 index 00000000..3cea3a79 --- /dev/null +++ b/ios/FluffyChat Share/ShareViewController.swift @@ -0,0 +1,335 @@ +import UIKit +import Social +import MobileCoreServices +import Photos + +class ShareViewController: SLComposeServiceViewController { + // TODO: IMPORTANT: This should be your host app bundle identifier + let hostAppBundleIdentifier = "com.kasem.sharing" + 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! + } +} + +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/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index a3537381..7134eee4 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -14,9 +14,22 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; AB0F2865DE230DE37373E0E0 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50DEFC207B70632D9C56ED78 /* Pods_Runner.framework */; }; + C1005C45261071B5002F4F32 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1005C44261071B5002F4F32 /* ShareViewController.swift */; }; + C1005C48261071B5002F4F32 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C1005C46261071B5002F4F32 /* MainInterface.storyboard */; }; + C1005C4C261071B5002F4F32 /* FluffyChat Share.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = C1005C42261071B5002F4F32 /* FluffyChat Share.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; C149567C25C7274F00A16396 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C149567B25C7274F00A16396 /* GoogleService-Info.plist */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + C1005C4A261071B5002F4F32 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = C1005C41261071B5002F4F32; + remoteInfo = "FluffyChat Share"; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -28,6 +41,17 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + C1005C4D261071B5002F4F32 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + C1005C4C261071B5002F4F32 /* FluffyChat Share.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -47,6 +71,11 @@ 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 = ""; }; 9DB2F3524376810E74C799A8 /* 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 = ""; }; + C1005C42261071B5002F4F32 /* FluffyChat Share.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "FluffyChat Share.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + C1005C44261071B5002F4F32 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; + C1005C47261071B5002F4F32 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; + C1005C49261071B5002F4F32 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C1005C53261072D4002F4F32 /* FluffyChat Share.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "FluffyChat Share.entitlements"; 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 = ""; }; 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 = ""; }; @@ -61,6 +90,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C1005C3F261071B5002F4F32 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -88,6 +124,7 @@ children = ( 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, + C1005C43261071B5002F4F32 /* FluffyChat Share */, 97C146EF1CF9000F007C117D /* Products */, E89DCAC000D371640E94E65B /* Pods */, 075EE1BE25359E34308E0B78 /* Frameworks */, @@ -98,6 +135,7 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, + C1005C42261071B5002F4F32 /* FluffyChat Share.appex */, ); name = Products; sourceTree = ""; @@ -119,6 +157,17 @@ path = Runner; sourceTree = ""; }; + C1005C43261071B5002F4F32 /* FluffyChat Share */ = { + isa = PBXGroup; + children = ( + C1005C53261072D4002F4F32 /* FluffyChat Share.entitlements */, + C1005C44261071B5002F4F32 /* ShareViewController.swift */, + C1005C46261071B5002F4F32 /* MainInterface.storyboard */, + C1005C49261071B5002F4F32 /* Info.plist */, + ); + path = "FluffyChat Share"; + sourceTree = ""; + }; E89DCAC000D371640E94E65B /* Pods */ = { isa = PBXGroup; children = ( @@ -144,22 +193,42 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, F9C8EE392B9AB471149C306E /* [CP] Embed Pods Frameworks */, + C1005C4D261071B5002F4F32 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( + C1005C4B261071B5002F4F32 /* PBXTargetDependency */, ); name = Runner; productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; }; + C1005C41261071B5002F4F32 /* FluffyChat Share */ = { + isa = PBXNativeTarget; + buildConfigurationList = C1005C51261071B5002F4F32 /* Build configuration list for PBXNativeTarget "FluffyChat Share" */; + buildPhases = ( + C1005C3E261071B5002F4F32 /* Sources */, + C1005C3F261071B5002F4F32 /* Frameworks */, + C1005C40261071B5002F4F32 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "FluffyChat Share"; + productName = "FluffyChat Share"; + productReference = C1005C42261071B5002F4F32 /* FluffyChat Share.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 1240; LastUpgradeCheck = 1020; ORGANIZATIONNAME = ""; TargetAttributes = { @@ -167,6 +236,9 @@ CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 1100; }; + C1005C41261071B5002F4F32 = { + CreatedOnToolsVersion = 12.4; + }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; @@ -183,6 +255,7 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, + C1005C41261071B5002F4F32 /* FluffyChat Share */, ); }; /* End PBXProject section */ @@ -200,6 +273,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C1005C40261071B5002F4F32 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C1005C48261071B5002F4F32 /* MainInterface.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -282,8 +363,24 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C1005C3E261071B5002F4F32 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C1005C45261071B5002F4F32 /* ShareViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + C1005C4B261071B5002F4F32 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C1005C41261071B5002F4F32 /* FluffyChat Share */; + targetProxy = C1005C4A261071B5002F4F32 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -301,6 +398,14 @@ name = LaunchScreen.storyboard; sourceTree = ""; }; + C1005C46261071B5002F4F32 /* MainInterface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C1005C47261071B5002F4F32 /* Base */, + ); + name = MainInterface.storyboard; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ @@ -358,6 +463,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; @@ -496,6 +602,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; @@ -528,6 +635,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; @@ -555,6 +663,102 @@ }; name = Release; }; + C1005C4E261071B5002F4F32 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "FluffyChat Share/FluffyChat Share.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEVELOPMENT_TEAM = 4NXF6Z997G; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "FluffyChat Share/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 0.28.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "im.fluffychat.app.FluffyChat-Share"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + C1005C4F261071B5002F4F32 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "FluffyChat Share/FluffyChat Share.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEVELOPMENT_TEAM = 4NXF6Z997G; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "FluffyChat Share/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 0.28.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "im.fluffychat.app.FluffyChat-Share"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + C1005C50261071B5002F4F32 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "FluffyChat Share/FluffyChat Share.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEVELOPMENT_TEAM = 4NXF6Z997G; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "FluffyChat Share/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 0.28.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "im.fluffychat.app.FluffyChat-Share"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Profile; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -578,6 +782,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + C1005C51261071B5002F4F32 /* Build configuration list for PBXNativeTarget "FluffyChat Share" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C1005C4E261071B5002F4F32 /* Debug */, + C1005C4F261071B5002F4F32 /* Release */, + C1005C50261071B5002F4F32 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 595592c6..e4ea89e7 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -22,6 +22,20 @@ $(FLUTTER_BUILD_NAME) CFBundleSignature ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + im.fluffychat.app.uris + CFBundleURLSchemes + + ShareMedia + im.fluffychat + + + CFBundleVersion $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS @@ -59,17 +73,6 @@ LaunchScreen UIMainStoryboardFile Main - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLSchemes - - im.fluffychat - - - UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements index 903def2a..eb0b314e 100644 --- a/ios/Runner/Runner.entitlements +++ b/ios/Runner/Runner.entitlements @@ -4,5 +4,13 @@ aps-environment development + com.apple.developer.associated-domains + + applinks:example.com + + com.apple.security.application-groups + + group.com.famedly.app + diff --git a/lib/views/homeserver_picker.dart b/lib/views/homeserver_picker.dart index 8b478fea..72a257db 100644 --- a/lib/views/homeserver_picker.dart +++ b/lib/views/homeserver_picker.dart @@ -104,9 +104,9 @@ class _HomeserverPickerState extends State { .any((flow) => flow.type == AuthenticationTypes.sso)) { final redirectUrl = kIsWeb ? html.window.location.href - : '${Uri.encodeQueryComponent(AppConfig.appOpenUrlScheme.toLowerCase() + '://sso')}'; + : AppConfig.appOpenUrlScheme.toLowerCase() + '://sso'; await launch( - '${Matrix.of(context).client.homeserver?.toString()}/_matrix/client/r0/login/sso/redirect?redirectUrl=$redirectUrl'); + '${Matrix.of(context).client.homeserver?.toString()}/_matrix/client/r0/login/sso/redirect?redirectUrl=${Uri.encodeQueryComponent(redirectUrl)}'); } } catch (e) { // ignore: unawaited_futures