diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 1d4c0a0..6533663 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -13,10 +13,5 @@ - - - - - diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index b66acbf..4491749 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -35,11 +35,6 @@ - - - - - @@ -54,10 +49,5 @@ - - - - - diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml index 1d4c0a0..6533663 100644 --- a/android/app/src/profile/AndroidManifest.xml +++ b/android/app/src/profile/AndroidManifest.xml @@ -13,10 +13,5 @@ - - - - - diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index dcf0c72..573e609 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -18,18 +18,6 @@ $(FLUTTER_BUILD_NAME) CFBundleSignature ???? - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLSchemes - - ShareMedia - - - - CFBundleVersion $(FLUTTER_BUILD_NUMBER) LSApplicationQueriesSchemes diff --git a/ios/ShareExtension/Base.lproj/MainInterface.storyboard b/ios/ShareExtension/Base.lproj/MainInterface.storyboard deleted file mode 100644 index 286a508..0000000 --- a/ios/ShareExtension/Base.lproj/MainInterface.storyboard +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/ShareExtension/Info.plist b/ios/ShareExtension/Info.plist deleted file mode 100644 index 00576e2..0000000 --- a/ios/ShareExtension/Info.plist +++ /dev/null @@ -1,21 +0,0 @@ - - - - - NSExtension - - NSExtensionAttributes - - NSExtensionActivationRule - - NSExtensionActivationSupportsFileWithMaxCount - 1 - - - NSExtensionMainStoryboard - MainInterface - NSExtensionPointIdentifier - com.apple.share-services - - - diff --git a/ios/ShareExtension/ShareExtension.entitlements b/ios/ShareExtension/ShareExtension.entitlements deleted file mode 100644 index 1db6944..0000000 --- a/ios/ShareExtension/ShareExtension.entitlements +++ /dev/null @@ -1,10 +0,0 @@ - - - - - com.apple.security.application-groups - - group.de.seemoo.ios.openhaystack - - - diff --git a/ios/ShareExtension/ShareViewController.swift b/ios/ShareExtension/ShareViewController.swift deleted file mode 100644 index 4535c28..0000000 --- a/ios/ShareExtension/ShareViewController.swift +++ /dev/null @@ -1,342 +0,0 @@ -// -// ShareViewController.swift -// ShareExtension -// -// Created by Max Granzow on 18.01.22. -// - -import UIKit -import Social -import MobileCoreServices -import Photos - -// Source: https://pub.dev/packages/receive_sharing_intent -class ShareViewController: SLComposeServiceViewController { - let hostAppBundleIdentifier = "de.seemoo.ios.openhaystack" - 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(fileURLType) { - handleFiles(content: content, attachment: attachment, index: index) - } else if attachment.hasItemConformingToTypeIdentifier(textContentType) { - handleText(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 - } -} diff --git a/lib/main.dart b/lib/main.dart index 373863e..eb2a6b3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:openhaystack_mobile/dashboard/dashboard_desktop.dart'; import 'package:openhaystack_mobile/dashboard/dashboard_mobile.dart'; import 'package:openhaystack_mobile/accessory/accessory_registry.dart'; @@ -53,32 +52,10 @@ class _AppLayoutState extends State { initState() { super.initState(); - _intentDataStreamSubscription = ReceiveSharingIntent.getMediaStream() - .listen(handleFileSharingIntent, onError: print); - ReceiveSharingIntent.getInitialMedia() - .then(handleFileSharingIntent); - var accessoryRegistry = Provider.of(context, listen: false); accessoryRegistry.loadAccessories(); } - Future handleFileSharingIntent(List files) async { - // Received a sharing intent with a number of files. - // Import the accessories for each device in sequence. - // If no files are shared do nothing - for (var file in files) { - if (file.type == SharedMediaType.FILE) { - // On iOS the file:// prefix has to be stripped to access the file path - String path = Platform.isIOS - ? Uri.decodeComponent(file.path.replaceFirst('file://', '')) - : file.path; - Navigator.push(context, MaterialPageRoute( - builder: (context) => ItemFileImport(filePath: path), - )); - } - } - } - @override void dispose() { _intentDataStreamSubscription?.cancel(); diff --git a/pubspec.lock b/pubspec.lock index be5ff95..691c999 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -648,14 +648,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" - receive_sharing_intent: - dependency: "direct main" - description: - name: receive_sharing_intent - sha256: "912bebb551bce75a14098891fd750305b30d53eba0d61cc70cd9973be9866e8d" - url: "https://pub.dev" - source: hosted - version: "1.4.5" share_plus: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index e3a8c86..80144c2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -56,7 +56,6 @@ dependencies: file_picker: ^5.2.7 # Sharing - receive_sharing_intent: ^1.4.5 share_plus: ^6.3.1 url_launcher: ^6.0.17 path_provider: ^2.0.8